summaryrefslogtreecommitdiffstats
path: root/testing/web-platform/tests/css/cssom-view
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /testing/web-platform/tests/css/cssom-view
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'testing/web-platform/tests/css/cssom-view')
-rw-r--r--testing/web-platform/tests/css/cssom-view/CaretPosition-001.html44
-rw-r--r--testing/web-platform/tests/css/cssom-view/DOMRectList.html24
-rw-r--r--testing/web-platform/tests/css/cssom-view/GetBoundingRect.html31
-rw-r--r--testing/web-platform/tests/css/cssom-view/HTMLBody-ScrollArea_quirksmode.html145
-rw-r--r--testing/web-platform/tests/css/cssom-view/HTMLImageElement-x-and-y-ignore-transforms.html30
-rw-r--r--testing/web-platform/tests/css/cssom-view/META.yml4
-rw-r--r--testing/web-platform/tests/css/cssom-view/MediaQueryList-addListener-handleEvent.html118
-rw-r--r--testing/web-platform/tests/css/cssom-view/MediaQueryList-addListener-removeListener.html161
-rw-r--r--testing/web-platform/tests/css/cssom-view/MediaQueryList-change-event-matches-value.html32
-rw-r--r--testing/web-platform/tests/css/cssom-view/MediaQueryList-extends-EventTarget-interop.html172
-rw-r--r--testing/web-platform/tests/css/cssom-view/MediaQueryList-extends-EventTarget.html118
-rw-r--r--testing/web-platform/tests/css/cssom-view/MediaQueryListEvent.html90
-rw-r--r--testing/web-platform/tests/css/cssom-view/Screen-pixelDepth-Screen-colorDepth001.html35
-rw-r--r--testing/web-platform/tests/css/cssom-view/add-background-attachment-fixed-during-smooth-scroll-ref.html21
-rw-r--r--testing/web-platform/tests/css/cssom-view/add-background-attachment-fixed-during-smooth-scroll.html47
-rw-r--r--testing/web-platform/tests/css/cssom-view/background-change-during-smooth-scroll.html46
-rw-r--r--testing/web-platform/tests/css/cssom-view/checkVisibility.html99
-rw-r--r--testing/web-platform/tests/css/cssom-view/client-props-inline-list-item.html26
-rw-r--r--testing/web-platform/tests/css/cssom-view/client-props-input.html36
-rw-r--r--testing/web-platform/tests/css/cssom-view/client-props-root-display-none-crash.html17
-rw-r--r--testing/web-platform/tests/css/cssom-view/client-props-root.html29
-rw-r--r--testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-001.html41
-rw-r--r--testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-002.html27
-rw-r--r--testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-003.html34
-rw-r--r--testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-vertical-rl-ref.html4
-rw-r--r--testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-vertical-rl.html17
-rw-r--r--testing/web-platform/tests/css/cssom-view/cssom-getBoxQuads-001.html61
-rw-r--r--testing/web-platform/tests/css/cssom-view/cssom-getBoxQuads-002.html23
-rw-r--r--testing/web-platform/tests/css/cssom-view/cssom-getClientRects-002.html26
-rw-r--r--testing/web-platform/tests/css/cssom-view/cssom-getClientRects.html23
-rw-r--r--testing/web-platform/tests/css/cssom-view/cssom-view-img-attributes-001.html46
-rw-r--r--testing/web-platform/tests/css/cssom-view/cssom-view-window-screen-interface.html31
-rw-r--r--testing/web-platform/tests/css/cssom-view/devicePixelRatio-undisplayed-iframe.tentative.html19
-rw-r--r--testing/web-platform/tests/css/cssom-view/dom-element-scroll.html100
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-001.html34
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-002.html40
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-003.html48
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-dynamic-anon-box.html44
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-ellipsis-in-inline-box.html36
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-float-in-relative.html23
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-float-in-table.html60
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-list-001.html83
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-mixed-font-sizes.html23
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-parameters.html51
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-subpixel.html59
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint-visibility-hidden-resizer.html12
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPoint.html208
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementFromPosition.html121
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementScroll-002.html57
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementScroll.html173
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-iframes.html83
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-htb-ltr.html18
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-htb-rtl.html18
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vlr-ltr.html18
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vlr-rtl.html18
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vrl-ltr.html18
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vrl-rtl.html18
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-invalid-cases.html56
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-shadowroot.html87
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-simple.html131
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-svg-text.html51
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-svg.html67
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint-table.html99
-rw-r--r--testing/web-platform/tests/css/cssom-view/elementsFromPoint.html150
-rw-r--r--testing/web-platform/tests/css/cssom-view/getBoundingClientRect-empty-inline.html35
-rw-r--r--testing/web-platform/tests/css/cssom-view/getBoundingClientRect-shy.html93
-rw-r--r--testing/web-platform/tests/css/cssom-view/getBoundingClientRect-svg.html62
-rw-r--r--testing/web-platform/tests/css/cssom-view/getClientRects-br-htb-ltr.html16
-rw-r--r--testing/web-platform/tests/css/cssom-view/getClientRects-br-htb-rtl.html16
-rw-r--r--testing/web-platform/tests/css/cssom-view/getClientRects-br-vlr-ltr.html16
-rw-r--r--testing/web-platform/tests/css/cssom-view/getClientRects-br-vlr-rtl.html16
-rw-r--r--testing/web-platform/tests/css/cssom-view/getClientRects-br-vrl-ltr.html16
-rw-r--r--testing/web-platform/tests/css/cssom-view/getClientRects-br-vrl-rtl.html16
-rw-r--r--testing/web-platform/tests/css/cssom-view/getClientRects-inline-atomic-child.html44
-rw-r--r--testing/web-platform/tests/css/cssom-view/getClientRects-inline-inline-child.html52
-rw-r--r--testing/web-platform/tests/css/cssom-view/getClientRects-inline.html55
-rw-r--r--testing/web-platform/tests/css/cssom-view/historical.html15
-rw-r--r--testing/web-platform/tests/css/cssom-view/htmlelement-offset-width-001.html29
-rw-r--r--testing/web-platform/tests/css/cssom-view/idlharness.html47
-rw-r--r--testing/web-platform/tests/css/cssom-view/iframe.html4
-rw-r--r--testing/web-platform/tests/css/cssom-view/inheritance.html21
-rw-r--r--testing/web-platform/tests/css/cssom-view/long_scroll_composited-ref.html41
-rw-r--r--testing/web-platform/tests/css/cssom-view/long_scroll_composited.html50
-rw-r--r--testing/web-platform/tests/css/cssom-view/matchMedia-display-none-iframe.html19
-rw-r--r--testing/web-platform/tests/css/cssom-view/matchMedia.html79
-rw-r--r--testing/web-platform/tests/css/cssom-view/mouseEvent-offsetXY-svg.html27
-rw-r--r--testing/web-platform/tests/css/cssom-view/mouseEvent.html42
-rw-r--r--testing/web-platform/tests/css/cssom-view/negativeMargins.html32
-rw-r--r--testing/web-platform/tests/css/cssom-view/offsetParent-block-in-inline.html14
-rw-r--r--testing/web-platform/tests/css/cssom-view/offsetParent_element_test.html181
-rw-r--r--testing/web-platform/tests/css/cssom-view/offsetTop-offsetLeft-nested-offsetParents.html40
-rw-r--r--testing/web-platform/tests/css/cssom-view/offsetTopLeft-border-box.html61
-rw-r--r--testing/web-platform/tests/css/cssom-view/offsetTopLeft-empty-inline-offset.html47
-rw-r--r--testing/web-platform/tests/css/cssom-view/offsetTopLeft-empty-inline.html32
-rw-r--r--testing/web-platform/tests/css/cssom-view/offsetTopLeft-inline.html54
-rw-r--r--testing/web-platform/tests/css/cssom-view/offsetTopLeft-leading-space-inline.html32
-rw-r--r--testing/web-platform/tests/css/cssom-view/offsetTopLeft-trailing-space-inline.html36
-rw-r--r--testing/web-platform/tests/css/cssom-view/offsetTopLeftInScrollableParent.html111
-rw-r--r--testing/web-platform/tests/css/cssom-view/outer-svg.html35
-rw-r--r--testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-computed.html19
-rw-r--r--testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-invalid.html19
-rw-r--r--testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-valid.html18
-rw-r--r--testing/web-platform/tests/css/cssom-view/position-sticky-root-scroller-with-scroll-behavior.html42
-rw-r--r--testing/web-platform/tests/css/cssom-view/pt-to-px-width.html21
-rw-r--r--testing/web-platform/tests/css/cssom-view/range-bounding-client-rect-with-display-contents.html39
-rw-r--r--testing/web-platform/tests/css/cssom-view/resize-event-on-initial-layout.html18
-rw-r--r--testing/web-platform/tests/css/cssom-view/resources/elementsFromPoint.js48
-rw-r--r--testing/web-platform/tests/css/cssom-view/resources/iframe1.html16
-rw-r--r--testing/web-platform/tests/css/cssom-view/resources/iframe2.html25
-rw-r--r--testing/web-platform/tests/css/cssom-view/resources/matchMedia.js60
-rw-r--r--testing/web-platform/tests/css/cssom-view/screenLeftTop.html16
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-back-to-initial-position.html44
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-behavior-default-css.html52
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-behavior-element.html189
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-behavior-main-frame-root.html196
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-behavior-main-frame-window.html170
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-behavior-scrollintoview-nested.html88
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth-navigation.html108
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth-positions.html179
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth.html61
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-behavior-subframe-root.html196
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-behavior-subframe-window.html172
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-no-layout-box.html21
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-overflow-clip-quirks-001.html23
-rw-r--r--testing/web-platform/tests/css/cssom-view/scroll-overflow-clip-quirks-002.html20
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-fixed.html258
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-partially-visible.html47
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-tb-writing-mode-and-rtl-direction.html111
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-tb-writing-mode.html106
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-inline-image.html26
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-scrollMargin.html78
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-scrollPadding.html77
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-shadow.html34
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-lr-writing-mode-and-rtl-direction.html106
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-lr-writing-mode.html108
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-rl-writing-mode-and-rtl-direction.html111
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-rl-writing-mode.html108
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-smooth.html101
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-stuck.tentative.html52
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-svg-shape.html33
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-lr-writing-mode-and-rtl-direction.html112
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-lr-writing-mode.html107
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-rl-writing-mode.html117
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollLeft-of-scroller-with-wider-scrollbar.html16
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollLeftTop.html136
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollTop-display-change-ref.html8
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollTop-display-change.html17
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollWidthHeight.xht111
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollWidthHeightWhenNotScrollable.xht122
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrolling-no-browsing-context.html33
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrolling-quirks-vs-nonquirks.html220
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-001-ref.html3
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-001.html17
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-002-ref.html2
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-002.html17
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollingElement.html126
-rw-r--r--testing/web-platform/tests/css/cssom-view/scrollintoview.html113
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/1x1-green.pngbin0 -> 135 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/1x1-lime.pngbin0 -> 135 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/1x1-maroon.pngbin0 -> 109 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/1x1-navy.pngbin0 -> 109 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/1x1-red.pngbin0 -> 135 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/1x1-white.pngbin0 -> 109 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/60x60-gg-rr.pngbin0 -> 224 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/60x60-green.pngbin0 -> 218 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/README28
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/a-green.css1
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/b-green.css1
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/c-red.css1
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/cat.pngbin0 -> 1883 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/import-green.css1
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/import-red.css1
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/pattern-grg-rgr-grg.pngbin0 -> 222 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/pattern-grg-rrg-rgg.pngbin0 -> 231 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/pattern-rgr-grg-rgr.pngbin0 -> 223 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/pattern-tr.pngbin0 -> 137 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/ruler-h-50%.pngbin0 -> 691 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/ruler-h-50px.pngbin0 -> 671 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/ruler-v-100px.pngbin0 -> 760 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/ruler-v-50px.pngbin0 -> 757 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/scroll-behavior.js87
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/square-purple.pngbin0 -> 92 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/square-teal.pngbin0 -> 92 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/square-white.pngbin0 -> 78 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/support/README4
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/support/swatch-green.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/support/swatch-red.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/swatch-blue.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/swatch-green.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/swatch-lime.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/swatch-orange.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/swatch-red.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/swatch-teal.pngbin0 -> 156 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/swatch-white.pngbin0 -> 85 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/swatch-yellow.pngbin0 -> 84 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/test-bl.pngbin0 -> 1368 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/test-br.pngbin0 -> 1045 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/test-inner-half-size.pngbin0 -> 180 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/test-outer.pngbin0 -> 2412 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/test-tl.pngbin0 -> 1025 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/support/test-tr.pngbin0 -> 1235 bytes
-rw-r--r--testing/web-platform/tests/css/cssom-view/table-border-collapse-client-width-height.html34
-rw-r--r--testing/web-platform/tests/css/cssom-view/table-border-separate-client-width-height.html34
-rw-r--r--testing/web-platform/tests/css/cssom-view/table-client-props.html83
-rw-r--r--testing/web-platform/tests/css/cssom-view/table-offset-props.html83
-rw-r--r--testing/web-platform/tests/css/cssom-view/table-scroll-props.html83
-rw-r--r--testing/web-platform/tests/css/cssom-view/table-with-border-client-width-height.html31
-rw-r--r--testing/web-platform/tests/css/cssom-view/ttwf-js-cssomview-getclientrects-length.html28
-rw-r--r--testing/web-platform/tests/css/cssom-view/window-screen-height-immutable.html27
-rw-r--r--testing/web-platform/tests/css/cssom-view/window-screen-height.html37
-rw-r--r--testing/web-platform/tests/css/cssom-view/window-screen-width-immutable.html27
-rw-r--r--testing/web-platform/tests/css/cssom-view/window-screen-width.html37
212 files changed, 10288 insertions, 0 deletions
diff --git a/testing/web-platform/tests/css/cssom-view/CaretPosition-001.html b/testing/web-platform/tests/css/cssom-view/CaretPosition-001.html
new file mode 100644
index 0000000000..d8e97a9234
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/CaretPosition-001.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<html>
+<head>
+<title>CSSOM View Module test:CaretPosition</title>
+<link rel="author" title="unbug" href="mailto:tidelgl@gmail.com" />
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-document-elementfrompoint">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style type="text/css">
+.box{
+ position: absolute;
+ top : 100px;
+ left : 400px;
+ width: 100px;
+ height: 100px;
+ background-color: #000;
+}
+</style>
+</head>
+<body>
+<div id="box" class="box"></div>
+<div class="box"></div>
+<div class="box"></div>
+<div class="box"></div>
+<div class="box"></div>
+<div class="box"></div>
+<div id="box2" class="box"></div>
+<div id="box3" class="box" style="left: 405px;background:red;"></div>
+<div id="log"></div>
+<script>
+test(function(){
+ assert_equals(document.elementFromPoint(400, 100),
+ document.getElementById('box2'),
+ "Expected value for element id is 'box2'");
+}, 'Element at (400, 100)');
+
+test(function(){
+ assert_equals(document.elementFromPoint(400, 900),
+ null,
+ "Expected value for element id is 'box2'");
+}, 'Element at (400, 900)');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/DOMRectList.html b/testing/web-platform/tests/css/cssom-view/DOMRectList.html
new file mode 100644
index 0000000000..dafced9947
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/DOMRectList.html
@@ -0,0 +1,24 @@
+<!DOCTYPE html>
+<title>CSSOM View APIs that return a DOMRectList</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=x>x</div>
+<script>
+setup(() => {
+ window.element = document.getElementById('x');
+});
+
+test(() => {
+ const domRectList = element.getClientRects();
+ assert_class_string(domRectList, 'DOMRectList');
+ assert_class_string(domRectList.item(0), 'DOMRect');
+}, 'Element getClientRects()');
+
+test(() => {
+ const range = new Range();
+ range.selectNodeContents(element);
+ const domRectList = range.getClientRects();
+ assert_class_string(domRectList, 'DOMRectList');
+ assert_class_string(domRectList.item(0), 'DOMRect');
+}, 'Range getClientRects()');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/GetBoundingRect.html b/testing/web-platform/tests/css/cssom-view/GetBoundingRect.html
new file mode 100644
index 0000000000..7286bd0988
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/GetBoundingRect.html
@@ -0,0 +1,31 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>getBoundingClientRect</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+
+ <style>
+ #foo {
+ margin: 0px 0px 0px 5px;
+ transform: translate(10px, 200px);
+ position: fixed;
+ left: 5px;
+ background-color: red;
+ }
+ </style>
+ </head>
+ <body>
+ <div id="foo">
+ FOO
+ </div>
+ <script>
+ test(function () {
+ var foo = document.getElementById("foo").getBoundingClientRect();
+ assert_equals(foo.left, 20);
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/HTMLBody-ScrollArea_quirksmode.html b/testing/web-platform/tests/css/cssom-view/HTMLBody-ScrollArea_quirksmode.html
new file mode 100644
index 0000000000..8255d13034
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/HTMLBody-ScrollArea_quirksmode.html
@@ -0,0 +1,145 @@
+<html>
+<script src="/resources/testharness.js" type="text/javascript"></script>
+<script src="/resources/testharnessreport.js" type="text/javascript"></script>
+<style type="text/css">
+ body {
+ border:1px solid black;
+ width:200px;
+ height:40px;
+
+ padding-bottom:50px;
+ padding-right:40px;
+ }
+ #elemSimple {
+ background:yellow;
+ width:60px;
+ height:30px;
+ }
+ #elemOverflow {
+ background:yellow;
+ width:250px;
+ height:150px;
+ }
+</style>
+<body id="thebody">
+ <div id="thediv"></div>
+</body>
+<script>
+// Testing for html body element's scroll- x, y, width, height behaviour in quirks mode
+// https://drafts.csswg.org/cssom-view/#potentially-scrollable
+// https://drafts.csswg.org/cssom-view/#dom-element-scrollheight
+test(function() {
+ // can i get the div element?
+ var thediv = document.getElementById("thediv");
+ assert_equals(thediv.id, "thediv");
+ // can i get the body element?
+ var thebody = document.getElementById("thebody");
+ assert_equals(thebody.id, "thebody");
+}, "Ensure that body element is loaded.")
+
+test(function() {
+ document.body.style.overflowY = "hidden";
+ assert_equals(document.body.style.overflowY, "hidden", "Could not set document.body.style.overflowY to 'hidden'.");
+ document.body.style.overflowY = "scroll";
+ assert_equals(document.body.style.overflowY, "scroll", "Could not set document.body.style.overflowY to 'scroll'.");
+ document.documentElement.style.overflowY = "scroll";
+ assert_equals(document.documentElement.style.overflowY, "scroll", "Could not set document.documentElement.style.overflow to 'scroll'.");
+ document.documentElement.style.overflowY = "";
+ document.body.style.overflowY = "";
+}, "Ensure that style.overflowY can be set properly.")
+
+test(function() {
+ assert_equals(document.compatMode, "BackCompat", "Should be in quirks mode.");
+}, "document.compatMode should be BackCompat in quirks.")
+
+test(function() {
+ var thebody = document.getElementById("thebody");
+ assert_equals(thebody.id, "thebody");
+ assert_equals(document.scrollingElement, thebody,
+ "scrollingElement in quirks mode should default to body element.");
+}, "document.scrollingElement should be body element in quirks.")
+
+test(function() {
+ document.documentElement.style.overflowY = "scroll";
+ assert_equals(document.documentElement.style.overflowY, "scroll", "Could not set document.documentElement.style.overflowY to 'scroll'.");
+
+ var thebody = document.getElementById("thebody");
+ assert_equals(thebody.id, "thebody");
+ thebody.style.overflowY="scroll";
+ assert_equals(document.body.style.overflowY, "scroll", "Could not set document.body.style.overflowY to 'scroll'.");
+ // Body and document now both have overflow != visible
+ // => body `potentially scrollable`
+
+ // In quirks, when body is not `potentially scrollable`
+ // document.scrollingElment returns the body, otherwise null
+ // https://drafts.csswg.org/cssom-view/#dom-document-scrollingelement
+ assert_equals(document.scrollingElement, null,
+ "In quirks, we would expect null here (because of potentially scrollable body)");
+}, "scrollingElement in quirks should be null when body is potentially scrollable.")
+
+test(function() {
+ document.documentElement.style.overflowY = "visible";
+ assert_equals(document.documentElement.style.overflowY, "visible");
+ assert_equals(document.scrollingElement, document.body);
+
+ document.documentElement.style.overflowY = "scroll";
+ assert_equals(document.documentElement.style.overflowY, "scroll");
+ document.body.style.overflowY = "visible";
+ assert_equals(document.body.style.overflowY, "visible");
+ assert_equals(document.scrollingElement, document.body);
+
+ document.documentElement.style.overflowY = "visible";
+ assert_equals(document.documentElement.style.overflowY, "visible");
+ assert_equals(document.scrollingElement, document.body);
+}, "scrollingElement in quirks should be body if any of document and body has a visible overflow.")
+
+// no overflow property set to `visible` => pot. scrollable
+test(function() {
+ document.body.style.overflowY = "scroll";
+ assert_equals(document.body.style.overflowY, "scroll");
+ document.documentElement.style.overflowY = "scroll";
+ assert_equals(document.documentElement.style.overflowY, "scroll");
+
+ assert_greater_than(window.innerHeight, 400, "Window not large enough for valid test run.");
+ assert_not_equals(document.body.scrollHeight, window.innerHeight);
+
+ var elem = document.getElementById("thediv");
+ elem.style.height = "170px";
+ assert_equals(elem.style.height, "170px");
+
+ oldScrollHeight = document.body.scrollHeight;
+ elem.style.height = "190px";
+ assert_equals(elem.style.height, "190px");
+ assert_equals(document.body.scrollHeight, oldScrollHeight+20);
+}, "When body potentially scrollable, document.body.scrollHeight changes when changing the height of the body content in quirks.")
+
+// any use of `visible` => not potentially scrollable
+function testNotPotScrollable (document_overflow, body_overflow) {
+ document.body.style.overflowY = body_overflow;
+ assert_equals(document.body.style.overflowY, body_overflow);
+ document.documentElement.style.overflowY = document_overflow;
+ assert_equals(document.documentElement.style.overflowY, document_overflow);
+
+ assert_greater_than(window.innerHeight, 400, "Window not large enough for valid test run.");
+ assert_equals(document.body.scrollHeight, window.innerHeight);
+
+ var elem = document.getElementById("thediv");
+ elem.style.height = "170px";
+ assert_equals(elem.style.height, "170px");
+ assert_equals(window.innerHeight, document.body.scrollHeight);
+
+ oldScrollHeight = document.body.scrollHeight;
+ elem.style.height = "190px";
+ assert_equals(elem.style.height, "190px");
+ assert_equals(window.innerHeight, document.body.scrollHeight);
+ assert_equals(document.body.scrollHeight, oldScrollHeight);
+}
+
+tests = [["visible", "scroll"], ["scroll", "visible"], ["visible", "visible"]];
+for (var i = 0; i < tests.length; i++) {
+ test( function () {
+ testNotPotScrollable (tests[i][0], tests[i][1]);
+ }, "When body not potentially scrollable, document.body.scrollHeight always equals window.innerHeight in quirks. (cond. "+tests[i][0]+", "+tests[i][1]+")")
+}
+</script>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/HTMLImageElement-x-and-y-ignore-transforms.html b/testing/web-platform/tests/css/cssom-view/HTMLImageElement-x-and-y-ignore-transforms.html
new file mode 100644
index 0000000000..45c79f3672
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/HTMLImageElement-x-and-y-ignore-transforms.html
@@ -0,0 +1,30 @@
+<!DOCTYPE html>
+<link rel="author" href="mailto:mrobinson@igalia.com.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlimageelement-interface">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1676952">
+<title>CSS OM View Test: HTMLImageElement's 'x' and 'y' property values should ignore transforms</title>
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+.transformed {
+ transform: translate(40px, 40px);
+}
+</style>
+
+<div id="container">
+ <img id="image" src="/images/green.png">
+</div>
+
+<script>
+ test(() => {
+ const unTransformedX = image.x;
+ const unTransformedY = image.y;
+
+ container.classList.add("transformed");
+
+ assert_equals(unTransformedX, image.x);
+ assert_equals(unTransformedY, image.y);
+ }, `HTMLImageElement's 'x' and 'y' property values ignore transforms`);
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/META.yml b/testing/web-platform/tests/css/cssom-view/META.yml
new file mode 100644
index 0000000000..a664494591
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/META.yml
@@ -0,0 +1,4 @@
+spec: https://drafts.csswg.org/cssom-view/
+suggested_reviewers:
+ - AutomatedTester
+ - plinss
diff --git a/testing/web-platform/tests/css/cssom-view/MediaQueryList-addListener-handleEvent.html b/testing/web-platform/tests/css/cssom-view/MediaQueryList-addListener-handleEvent.html
new file mode 100644
index 0000000000..43b144a9ae
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/MediaQueryList-addListener-handleEvent.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="flags" content="dom">
+<title>CSS Test: CSSOM View MediaQueryList::addListener with handleEvent</title>
+<link rel="help" href="https://dom.spec.whatwg.org/#callbackdef-eventlistener">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/matchMedia.js"></script>
+<div id="log"></div>
+<script>
+"use strict";
+setup({ allow_uncaught_exception: true });
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let _this;
+ let _event;
+ const listener = {
+ handleEvent(event) {
+ _this = this;
+ _event = event;
+ },
+ };
+
+ mql.addListener(listener);
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+
+ assert_equals(_this, listener);
+ assert_equals(_event.media, mql.media);
+ assert_equals(_event.matches, mql.matches);
+}, "calls handleEvent method of event listener");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = 0;
+ mql.addListener({
+ get handleEvent() {
+ calls++;
+ return function() {};
+ },
+ });
+ assert_equals(calls, 0);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 1);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 2);
+}, "looks up handleEvent method on every event dispatch");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = 0;
+ const listener = () => {
+ calls++;
+ };
+
+ Object.defineProperty(listener, "handleEvent", {
+ get: t.unreached_func("handleEvent method should not be looked up on functions"),
+ });
+ mql.addListener(listener);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 1);
+}, "doesn't look up handleEvent method on callable event listeners");
+
+const uncaught_error_test = async (t, mql, getHandleEvent) => {
+ const eventWatcher = new EventWatcher(t, window, "error", waitForChangesReported);
+ const errorPromise = eventWatcher.wait_for("error");
+
+ let calls = 0;
+ const listener = {
+ get handleEvent() {
+ calls++;
+ return getHandleEvent();
+ },
+ };
+
+ try {
+ mql.addListener(listener);
+ triggerMQLEvent(mql);
+
+ const event = await errorPromise;
+ throw event.error;
+ } finally {
+ assert_equals(calls, 1, "handleEvent property was not looked up");
+ }
+};
+
+promise_test(async t => {
+ const error = { name: "test" };
+ const mql = await createMQL(t);
+
+ return promise_rejects_exactly(t, error,
+ uncaught_error_test(t, mql, () => { throw error; }));
+}, "rethrows errors when getting handleEvent");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+ const global = getWindow(mql);
+ return promise_rejects_js(t, global.TypeError,
+ uncaught_error_test(t, mql, () => false));
+}, "throws if handleEvent is falsy and not callable");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+ const global = getWindow(mql);
+ return promise_rejects_js(t, global.TypeError,
+ uncaught_error_test(t, mql, () => "str"));
+}, "throws if handleEvent is thruthy and not callable");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/MediaQueryList-addListener-removeListener.html b/testing/web-platform/tests/css/cssom-view/MediaQueryList-addListener-removeListener.html
new file mode 100644
index 0000000000..c705206bba
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/MediaQueryList-addListener-removeListener.html
@@ -0,0 +1,161 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="flags" content="dom">
+<title>CSS Test: CSSOM View MediaQueryList::{add,remove}Listener</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#the-mediaquerylist-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/matchMedia.js"></script>
+<div id="log"></div>
+<script>
+"use strict";
+
+test(() => {
+ const mql = window.matchMedia("(min-width: 100px)");
+
+ mql.addListener(null);
+ mql.addListener(undefined);
+
+ mql.removeListener(null);
+ mql.removeListener(undefined);
+}, "EventListener parameter is optional");
+
+promise_test(async t => {
+ const iframe = await createIFrame(t, 200, 100);
+ const heightMQL = iframe.contentWindow.matchMedia("(max-height: 50px)");
+ const widthMQL = iframe.contentWindow.matchMedia("(min-width: 150px)");
+
+ let heightEvent;
+ let widthEvent;
+
+ heightMQL.addListener(event => {
+ heightEvent = event;
+ });
+ widthMQL.addListener(event => {
+ widthEvent = event;
+ });
+
+ assert_false(heightMQL.matches);
+ assert_true(widthMQL.matches);
+
+ iframe.height = "50"; // 200x100 => 200x50
+ await waitForChangesReported();
+
+ assert_equals(heightEvent.media, heightMQL.media);
+ assert_true(heightEvent.matches);
+ assert_true(heightMQL.matches);
+ assert_true(widthMQL.matches);
+
+ iframe.width = "100"; // 200x50 => 100x50
+ await waitForChangesReported();
+
+ assert_equals(widthEvent.media, widthMQL.media);
+ assert_false(widthEvent.matches);
+ assert_false(widthMQL.matches);
+ assert_true(heightMQL.matches);
+}, "listeners are called when <iframe> is resized");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let eventsCount = 0;
+ mql.addListener(() => {
+ eventsCount++;
+ });
+
+ for (let i = 1; i <= 10; i++) {
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(eventsCount, i);
+ }
+}, "listeners are called correct number of times");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+ const calls = [];
+
+ mql.addListener(() => {
+ calls.push("1st");
+ });
+ mql.addListener(() => {
+ calls.push("2nd");
+ });
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+
+ assert_array_equals(calls, ["1st", "2nd"]);
+}, "listeners are called in order they were added");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let called = 0;
+ const listener = () => {
+ called++;
+ };
+
+ mql.addListener(listener);
+ mql.addListener(listener);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+
+ assert_equals(called, 1);
+}, "listener that was added twice is called only once");
+
+promise_test(async t => {
+ const iframe = await createIFrame(t, 100);
+ const media = `(min-width: 200px)`;
+
+ const mql1 = iframe.contentWindow.matchMedia(media);
+ const mql2 = iframe.contentWindow.matchMedia(media);
+ const calls = [];
+
+ mql2.addListener(() => {
+ calls.push("mql2");
+ });
+
+ mql1.addListener(() => {
+ calls.push("mql1");
+ });
+
+ iframe.width = "200"; // 100x100 => 200x100
+ await waitForChangesReported();
+
+ assert_array_equals(calls, ["mql1", "mql2"]);
+}, "listeners are called in order their MQLs were created");
+
+promise_test(async t => {
+ const iframe = await createIFrame(t, 200);
+ const media = `(max-height: 150px)`;
+
+ const mql1 = iframe.contentWindow.matchMedia(media);
+ const mql2 = iframe.contentWindow.matchMedia(media);
+
+ let calls = 0;
+ const listener = () => {
+ calls++;
+ };
+
+ mql1.addListener(listener);
+ mql2.removeListener(listener);
+
+ iframe.height = "50"; // 200x200 => 200x50
+ await waitForChangesReported();
+
+ assert_equals(calls, 1);
+}, "removing listener from one MQL doesn't remove it from all MQLs");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+ const listener = t.unreached_func("should not be called");
+
+ mql.addListener(listener);
+ mql.removeListener(listener);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+}, "MediaQueryList::removeListener removes added listener");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/MediaQueryList-change-event-matches-value.html b/testing/web-platform/tests/css/cssom-view/MediaQueryList-change-event-matches-value.html
new file mode 100644
index 0000000000..d96c6bdff7
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/MediaQueryList-change-event-matches-value.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<title>MediaQueryList.changed is correct for all lists in the document even during a change event handler</title>
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1648839">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#evaluate-media-queries-and-report-changes">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/matchMedia.js"></script>
+<body>
+<script>
+ promise_test(async t => {
+ // Create two identical media queries.
+ let mql = await createMQL(t);
+ let mql2 = getWindow(mql).matchMedia(mql.media);
+
+ let changeEvents = 0;
+
+ let check = t.step_func(function() {
+ changeEvents++;
+ assert_equals(mql.matches, mql2.matches, "Value of .matches should match"); // No pun intended
+ });
+
+ mql.addListener(check);
+ mql2.addListener(check);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+
+ assert_equals(changeEvents, 2, "Should've fired the change event in both MediaQueryLists");
+ });
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/MediaQueryList-extends-EventTarget-interop.html b/testing/web-platform/tests/css/cssom-view/MediaQueryList-extends-EventTarget-interop.html
new file mode 100644
index 0000000000..2ac4b46647
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/MediaQueryList-extends-EventTarget-interop.html
@@ -0,0 +1,172 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="flags" content="dom">
+<title>CSS Test: CSSOM View MediaQueryList extends EventTarget (interop)</title>
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#the-mediaquerylist-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/matchMedia.js"></script>
+<div id="log"></div>
+<script>
+"use strict";
+
+test(() => {
+ const mql = window.matchMedia("all");
+
+ let receivedEvent;
+ mql.addListener(event => {
+ receivedEvent = event;
+ });
+
+ const dispatchedEvent = new Event("change");
+ mql.dispatchEvent(dispatchedEvent);
+
+ assert_equals(receivedEvent, dispatchedEvent);
+}, "dispatchEvent triggers listener added with addListener");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = 0;
+ const listener = {
+ handleEvent() {
+ calls++;
+ },
+ };
+
+ mql.addListener(listener);
+ mql.addEventListener("change", listener);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 1, "triggerMQLEvent");
+
+ mql.dispatchEvent(new Event("change"));
+ assert_equals(calls, 2, "dispatchEvent");
+}, "listener added with addListener and addEventListener is called once");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = 0;
+ const listener = () => {
+ calls++;
+ };
+
+ mql.addListener(listener);
+ mql.addEventListener("change", listener, true);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 2, "triggerMQLEvent");
+
+ mql.dispatchEvent(new Event("change"));
+ assert_equals(calls, 4, "dispatchEvent");
+}, "listener added with addListener and addEventListener (capture) is called twice");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = 0;
+ const listener = {
+ handleEvent() {
+ calls++;
+ },
+ };
+
+ mql.addListener(listener);
+ mql.removeEventListener("change", listener);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 0, "triggerMQLEvent");
+
+ mql.dispatchEvent(new Event("change"));
+ assert_equals(calls, 0, "dispatchEvent");
+}, "removeEventListener removes listener added with addListener");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = 0;
+ const listener = () => {
+ calls++;
+ };
+
+ mql.addListener(listener);
+ mql.removeEventListener("change", listener, true);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 1, "triggerMQLEvent");
+
+ mql.dispatchEvent(new Event("change"));
+ assert_equals(calls, 2, "dispatchEvent");
+}, "removeEventListener (capture) doesn't remove listener added with addListener");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = 0;
+ const listener = {
+ handleEvent() {
+ calls++;
+ },
+ };
+
+ mql.addEventListener("change", listener);
+ mql.removeListener(listener);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 0, "triggerMQLEvent");
+
+ mql.dispatchEvent(new Event("change"));
+ assert_equals(calls, 0, "dispatchEvent");
+}, "removeListener removes listener added with addEventListener");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = 0;
+ const listener = () => {
+ calls++;
+ };
+
+ mql.addEventListener("change", listener, true);
+ mql.removeListener(listener);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 1, "triggerMQLEvent");
+
+ mql.dispatchEvent(new Event("change"));
+ assert_equals(calls, 2, "dispatchEvent");
+}, "removeListener doesn't remove listener added with addEventListener (capture)");
+
+// See:
+// * https://github.com/whatwg/dom/issues/746
+// * https://bugzilla.mozilla.org/show_bug.cgi?id=1492446
+// * https://bugs.chromium.org/p/chromium/issues/detail?id=949432
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = [];
+ mql.addListener(() => {
+ calls.push("addListener");
+ });
+ mql.addEventListener("change", {
+ handleEvent() {
+ calls.push("addEventListener");
+ },
+ }, true);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_array_equals(calls, ["addEventListener", "addListener"], "triggerMQLEvent");
+
+ calls = [];
+ mql.dispatchEvent(new Event("change"));
+ assert_array_equals(calls, ["addEventListener", "addListener"], "dispatchEvent");
+}, "capturing event listener fires before non-capturing listener at target");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/MediaQueryList-extends-EventTarget.html b/testing/web-platform/tests/css/cssom-view/MediaQueryList-extends-EventTarget.html
new file mode 100644
index 0000000000..34b6ef15c0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/MediaQueryList-extends-EventTarget.html
@@ -0,0 +1,118 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="flags" content="dom">
+<title>CSS Test: CSSOM View MediaQueryList extends EventTarget</title>
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#the-mediaquerylist-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/matchMedia.js"></script>
+<div id="log"></div>
+<script>
+"use strict";
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let _event;
+ mql.onchange = event => {
+ _event = event;
+ };
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(_event.media, mql.media);
+ assert_equals(_event.matches, mql.matches);
+}, "onchange adds listener");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = 0;
+ mql.onchange = () => {
+ calls++;
+ };
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 1);
+
+ mql.onchange = null;
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 1);
+}, "onchange removes listener");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = 0;
+ mql.addEventListener("change", {
+ handleEvent() {
+ calls++;
+ },
+ });
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 1);
+}, 'listeners for "change" type are called');
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+ mql.addEventListener("matches", t.unreached_func("should not be called"));
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+}, 'listeners with different type are not called');
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let calls = 0;
+ mql.addEventListener("change", {
+ handleEvent() {
+ calls++;
+ },
+ }, {once: true});
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 1);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+ assert_equals(calls, 1);
+}, 'addEventListener "once" option is respected');
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+ const listener = t.unreached_func("should not be called");
+
+ mql.addEventListener("change", listener);
+ mql.removeEventListener("change", listener);
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+}, "removeEventListener removes listener");
+
+test(() => {
+ const mql = window.matchMedia("all");
+
+ let receivedEvent;
+ mql.addEventListener("custom", event => {
+ receivedEvent = event;
+ event.preventDefault();
+ }, true);
+
+ const dispatchedEvent = new CustomEvent("custom", {
+ cancelable: true,
+ detail: {},
+ });
+
+ const defaultAction = mql.dispatchEvent(dispatchedEvent);
+
+ assert_equals(receivedEvent, dispatchedEvent);
+ assert_false(defaultAction);
+}, "dispatchEvent works as expected");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/MediaQueryListEvent.html b/testing/web-platform/tests/css/cssom-view/MediaQueryListEvent.html
new file mode 100644
index 0000000000..0afa09f087
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/MediaQueryListEvent.html
@@ -0,0 +1,90 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="flags" content="dom">
+<title>CSS Test: CSSOM View MediaQueryListEvent</title>
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#mediaquerylistevent">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/matchMedia.js"></script>
+<div id="log"></div>
+<script>
+"use strict";
+
+test(() => {
+ assert_equals(new MediaQueryListEvent("test").type, "test");
+}, 'type can be different from "change"');
+
+test(() => {
+ const event = new MediaQueryListEvent("change");
+
+ assert_equals(event.media, "");
+ assert_false(event.matches);
+ assert_false(event.bubbles);
+ assert_false(event.cancelable);
+}, "init dictionary default values");
+
+test(() => {
+ const event = new MediaQueryListEvent("change", {
+ media: "test",
+ matches: true,
+ bubbles: true,
+ cancelable: true,
+ });
+
+ assert_equals(event.media, "test");
+ assert_true(event.matches);
+ assert_true(event.bubbles);
+ assert_true(event.cancelable);
+}, "init dictionary overrides");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let _event;
+ mql.addListener(event => {
+ _event = event;
+ });
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+
+ assert_true(_event instanceof getWindow(mql).MediaQueryListEvent);
+ assert_equals(_event.type, "change");
+ assert_false(_event.bubbles);
+ assert_false(_event.cancelable);
+}, "argument of addListener");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let _event;
+ mql.onchange = event => {
+ _event = event;
+ };
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+
+ assert_true(_event instanceof getWindow(mql).MediaQueryListEvent);
+ assert_equals(_event.type, "change");
+ assert_false(_event.bubbles);
+ assert_false(_event.cancelable);
+}, "argument of onchange");
+
+promise_test(async t => {
+ const mql = await createMQL(t);
+
+ let _event;
+ mql.addEventListener("change", event => {
+ _event = event;
+ });
+
+ triggerMQLEvent(mql);
+ await waitForChangesReported();
+
+ assert_true(_event instanceof getWindow(mql).MediaQueryListEvent);
+ assert_equals(_event.type, "change");
+ assert_false(_event.bubbles);
+ assert_false(_event.cancelable);
+}, 'constructor of "change" event');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/Screen-pixelDepth-Screen-colorDepth001.html b/testing/web-platform/tests/css/cssom-view/Screen-pixelDepth-Screen-colorDepth001.html
new file mode 100644
index 0000000000..f394560ff1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/Screen-pixelDepth-Screen-colorDepth001.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM View Module test:Screen-pixelDepth,Screen-colorDepth</title>
+ <link rel="author" title="unbug" href="mailto:tidelgl@gmail.com" />
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#the-screen-interface">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style type="text/css">
+ </style>
+</head>
+<body>
+ <p>This case tests the Screen pixelDepth and colorDepth</p>
+ <p>The test passes if the value is either 24 or 30</p>
+ <div id="log"></div>
+ <script>
+ test(function(){
+ let colorDepth = testColorDepth();
+ assert_true(colorDepth == 24 || colorDepth == 30, "Expected value for colorDepth is either 24 or 30");
+ },'testColorDepth');
+ test(function(){
+ let pixelDepth = testPixelDepth();
+ assert_true(pixelDepth == 24 || pixelDepth == 30, "Expected value for pixelDepth is either 24 or 30");
+ },'testPixelDepth');
+ function testColorDepth(){
+ var colorDepth = window.screen.colorDepth;
+ return colorDepth;
+ }
+ function testPixelDepth(){
+ var pixelDepth = window.screen.pixelDepth;
+ return pixelDepth;
+ }
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/add-background-attachment-fixed-during-smooth-scroll-ref.html b/testing/web-platform/tests/css/cssom-view/add-background-attachment-fixed-during-smooth-scroll-ref.html
new file mode 100644
index 0000000000..8bcbf41102
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/add-background-attachment-fixed-during-smooth-scroll-ref.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<title>Add background-attachment:fixed during smooth scroll</title>
+<style>
+#container {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ background: linear-gradient(green, blue);
+ background-attachment: fixed;
+}
+#content {
+ width: 7500px;
+ height: 7500px;
+}
+</style>
+<div id="container">
+ <div id="content">Content</div>
+</div>
+<script>
+container.scrollTop = 6000;
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/add-background-attachment-fixed-during-smooth-scroll.html b/testing/web-platform/tests/css/cssom-view/add-background-attachment-fixed-during-smooth-scroll.html
new file mode 100644
index 0000000000..6600c0e055
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/add-background-attachment-fixed-during-smooth-scroll.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<html class="reftest-wait">
+<title>Add background-attachment:fixed during smooth scroll</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dictdef-scrolltooptions">
+<link rel="match" href="add-background-attachment-fixed-during-smooth-scroll-ref.html">
+<meta name="fuzzy" content="maxDifference=0-2; totalPixels=0-27000">
+<script src="/common/reftest-wait.js"></script>
+<style>
+#container {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ background: linear-gradient(green, blue);
+ will-change: scroll-position;
+}
+#content {
+ width: 7500px;
+ height: 7500px;
+}
+</style>
+<script>
+function startSmoothScroll() {
+ var scrollToOptions = {behavior: "smooth", top: 6000};
+ container.scrollTo(scrollToOptions);
+ requestAnimationFrame(preventCompositedScrolling);
+}
+
+function preventCompositedScrolling() {
+ container.style.backgroundAttachment = "fixed";
+ requestAnimationFrame(waitForSmoothScrollEnd);
+}
+
+function waitForSmoothScrollEnd() {
+ if (container.scrollTop == 6000) {
+ takeScreenshot();
+ } else {
+ window.requestAnimationFrame(waitForSmoothScrollEnd);
+ }
+}
+
+onload = () => {
+ requestAnimationFrame(startSmoothScroll);
+}
+</script>
+<div id="container">
+ <div id="content">Content</div>
+</div>
diff --git a/testing/web-platform/tests/css/cssom-view/background-change-during-smooth-scroll.html b/testing/web-platform/tests/css/cssom-view/background-change-during-smooth-scroll.html
new file mode 100644
index 0000000000..b5c28c77f8
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/background-change-during-smooth-scroll.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<title>Background change from opaque to transparent during smooth scroll</title>
+<link rel=help href="https://drafts.csswg.org/cssom-view/#dictdef-scrolltooptions">
+<style>
+#container {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ background: white;
+}
+#content {
+ width: 7500px;
+ height: 7500px;
+}
+</style>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+var t = async_test('background change during smooth scroll');
+
+function startSmoothScroll() {
+ var scrollToOptions = {behavior: "smooth", top: 6000};
+ container.scrollTo(scrollToOptions);
+ requestAnimationFrame(preventCompositedScrolling);
+}
+
+function preventCompositedScrolling() {
+ container.style.background = "transparent";
+ requestAnimationFrame(waitForSmoothScrollEnd);
+}
+
+function waitForSmoothScrollEnd() {
+ if (container.scrollTop == 6000) {
+ t.done();
+ } else {
+ window.requestAnimationFrame(waitForSmoothScrollEnd);
+ }
+}
+
+onload = () => {
+ requestAnimationFrame(startSmoothScroll);
+}
+</script>
+<div id="container">
+ <div id="content">Content</div>
+</div>
diff --git a/testing/web-platform/tests/css/cssom-view/checkVisibility.html b/testing/web-platform/tests/css/cssom-view/checkVisibility.html
new file mode 100644
index 0000000000..3ab1cc7dc2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/checkVisibility.html
@@ -0,0 +1,99 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://github.com/w3c/csswg-drafts/issues/6850">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div id=visibilityhidden style="visibility:hidden">hello</div>
+
+<div style="content-visibility:hidden">
+ <div id=cvhidden>hello</div>
+</div>
+
+<div style="content-visibility:auto">
+ <div id=cvauto>hello</div>
+</div>
+
+<div id=displaynone style="display:none">hello</div>
+
+<div id=opacityzero style="opacity:0">hello</div>
+
+<div style="content-visibility:hidden">
+ <div id=cvhiddenchildwithupdate></div>
+</div>
+
+<div style="content-visibility:hidden" id=cvhiddenwithupdate></div>
+
+<div style="height:10000px">spacer</div>
+
+<div style="content-visibility:auto">
+ <div id=cvautooffscreen>hello</div>
+</div>
+
+<div id=cvautocontainer>
+ <div id=cvautochild></div>
+</div>
+
+<div style="content-visibility:auto">
+ <div style="content-visibility:auto">
+ <div id=nestedcvautochild></div>
+ </div>
+</div>
+
+<script>
+test(() => {
+ assert_false(visibilityhidden.checkVisibility({
+ checkVisibilityCSS: true
+ }), 'checkVisibilityCSS:true');
+ assert_true(visibilityhidden.checkVisibility({
+ checkVisibilityCSS: false
+ }), 'checkVisibilityCSS:false');
+}, 'checkVisibility on visibility:hidden element.');
+
+test(() => {
+ assert_false(cvhidden.checkVisibility());
+}, 'checkVisibility on content-visibility:hidden element.');
+
+test(() => {
+ assert_true(cvauto.checkVisibility());
+}, 'checkVisibility on content-visibility:auto element.');
+
+test(() => {
+ assert_true(cvautooffscreen.checkVisibility());
+}, 'checkVisibility on content-visibility:auto element which is outside the viewport.');
+
+test(() => {
+ assert_false(displaynone.checkVisibility());
+}, 'checkVisibility on display:none element.');
+
+test(() => {
+ assert_false(opacityzero.checkVisibility({
+ checkOpacity: true
+ }), 'checkOpacity:true');
+ assert_true(opacityzero.checkVisibility({
+ checkOpacity: false
+ }), 'checkOpacity:false');
+}, 'checkVisibility on opacity:0 element.');
+
+test(() => {
+ cvautocontainer.style.contentVisibility = 'auto';
+ cvautochild.style.visibility = 'hidden';
+ assert_false(cvautochild.checkVisibility({checkVisibilityCSS: true}));
+ cvautochild.style.visibility = 'visible';
+ assert_true(cvautochild.checkVisibility({checkVisibilityCSS: true}));
+}, 'checkVisibility on content-visibility:auto with visibility:hidden inside.');
+
+test(() => {
+ assert_true(nestedcvautochild.checkVisibility());
+}, 'checkVisibility on nested content-visibility:auto containers reports that the content is visible.');
+
+test(() => {
+ cvhiddenchildwithupdate.getBoundingClientRect();
+ assert_false(cvhiddenchildwithupdate.checkVisibility());
+}, 'checkVisibility on content-visibility:hidden child after forced layout update.');
+
+test(() => {
+ cvhiddenwithupdate.getBoundingClientRect();
+ assert_true(cvhiddenwithupdate.checkVisibility());
+}, 'checkVisibility on content-visibility:hidden element after forced layout update.');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/client-props-inline-list-item.html b/testing/web-platform/tests/css/cssom-view/client-props-inline-list-item.html
new file mode 100644
index 0000000000..725bb787a5
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/client-props-inline-list-item.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<title>client* returns the same for non-replaced inlines regardless of list-item-ness</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1581467">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ .li {
+ display: inline list-item;
+ }
+</style>
+<div style="position: absolute"><span>Foo</span></div>
+<div style="position: absolute"><span class="li">Foo</span></div>
+<script>
+test(() => {
+ let first = document.querySelector("span");
+ let second = document.querySelector(".li");
+
+ assert_equals(first.clientWidth, second.clientWidth, "clientWidth should match");
+ assert_equals(first.clientHeight, second.clientHeight, "clientHeight should match");
+ assert_equals(first.clientTop, second.clientTop, "clientTop should match");
+ assert_equals(first.clientLeft, second.clientLeft, "clientLeft should match");
+}, "client* returns the same for non-replaced inlines regardless of list-item-ness");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/client-props-input.html b/testing/web-platform/tests/css/cssom-view/client-props-input.html
new file mode 100644
index 0000000000..d092267d3c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/client-props-input.html
@@ -0,0 +1,36 @@
+<!doctype html>
+<title>client* on input / textarea</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1654769">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ input, textarea {
+ -webkit-appearance: none;
+ appearance: none;
+ height: 200px;
+ width: 300px;
+ border-style: solid;
+ border-width: 10px 20px;
+ padding: 2px;
+ box-sizing: content-box;
+ }
+ .block { display: block; }
+</style>
+<input>
+<textarea></textarea>
+<input class="block">
+<textarea class="block"></textarea>
+<script>
+test(() => {
+ for (let element of document.querySelectorAll("input, textarea")) {
+ let description = `${element.nodeName} ${element.className}: `;
+ assert_equals(element.clientWidth, 304, description + "clientWidth should be the padding box");
+ assert_equals(element.clientHeight, 204, description + "clientHeight should be the padding box");
+ assert_equals(element.clientTop, 10, description + "clientTop should include the border offset");
+ assert_equals(element.clientLeft, 20, description + "clientLeft should include the border offset");
+ }
+}, "client* on input / textarea");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/client-props-root-display-none-crash.html b/testing/web-platform/tests/css/cssom-view/client-props-root-display-none-crash.html
new file mode 100644
index 0000000000..654e321e42
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/client-props-root-display-none-crash.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1655741">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<title>Shouldn't assert when asking for the client* props on the scrolling element</title>
+<style>
+ :root { display: none }
+</style>
+<script>
+ document.scrollingElement.clientTop;
+ document.scrollingElement.clientLeft;
+ document.scrollingElement.clientHeight;
+ document.scrollingElement.clientWidth;
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/client-props-root.html b/testing/web-platform/tests/css/cssom-view/client-props-root.html
new file mode 100644
index 0000000000..875522ec7c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/client-props-root.html
@@ -0,0 +1,29 @@
+<!doctype html>
+<title>client* on the scrolling element</title>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1654769">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ :root {
+ /* Ensure the width of the root element's box is smaller than the viewport. */
+ margin: 5px;
+ /* Remove the scrollbars so that we can test {Width, Height}. */
+ scrollbar-width: none;
+ /* Would love to add a border but https://github.com/w3c/csswg-drafts/issues/5363 */
+ }
+</style>
+<div style="height: 200vh; width: 200vw"></div>
+<script>
+test(function() {
+ scrollTo(100, 100);
+ assert_equals(document.documentElement.clientTop, 0, "Client top doesn't depend on scrolling");
+ assert_equals(document.documentElement.clientLeft, 0, "Client left doesn't depend on scrolling");
+ assert_equals(document.documentElement.clientWidth, window.innerWidth, "Without scrollbars, client width should match viewport width");
+ assert_equals(document.documentElement.clientHeight, window.innerHeight, "Without scrollbars, client height should match viewport height");
+}, "client* properties on the root element");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-001.html b/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-001.html
new file mode 100644
index 0000000000..7d96540adf
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-001.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM View - 6.1 - getBoundingClientRect tests</title>
+ <meta charset="utf-8">
+ <link rel="author" title="Chris Wu" href="mailto:pwx.frontend@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#dom-element-getboundingclientrect">
+ <meta name="flags" content="dom">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style type="text/css">
+ #testItem {
+ width: 279px;
+ height: 188px;
+ margin: 100px 0 0 178px;
+ background-color: purple;
+ font-size: 26px;
+ font-weight: bold;
+ text-align: center;
+ line-height: 188px;
+ }
+ </style>
+</head>
+<body>
+ <div id="testItem">test item</div>
+ <div id="log"></div>
+ <script>
+ var titem = document.getElementById('testItem').getBoundingClientRect();
+ test(
+ function(){
+ assert_equals(titem.bottom - titem.top, titem.height, "height should equal bottom minus top")
+ }, "getBoundingClientRect() should return a DOMRect where height=bottom-top"
+ );
+ test(
+ function(){
+ assert_equals(titem.right - titem.left, titem.width, "width should equal right minus left")
+ }, "getBoundingClientRect() should return a DOMRect where width=right-left"
+ )
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-002.html b/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-002.html
new file mode 100644
index 0000000000..a7c3568969
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-002.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSS Test (CSSOM View): getBoundingClientRect of element outside DOM</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#dom-element-getclientrects">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#dom-element-getboundingclientrect">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="Calling getBoundingClientRect on an element that is outside of the DOM (and therefore does not have an associated layout box) should result in an all-zeroes DOMRect and should definitely not throw an error.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ test(
+ function () {
+ var rect = document.createElement('div').getBoundingClientRect();
+ assert_equals(rect.x, 0, "DOMRect's x should be zero");
+ assert_equals(rect.y, 0, "DOMRect's y should be zero");
+ assert_equals(rect.width, 0, "DOMRect's width should be zero");
+ assert_equals(rect.height, 0, "DOMRect's height should be zero");
+ },
+ "getBoundingClientRect on a newly-created Element not yet inserted into the DOM should return an all-zeroes DOMRect"
+ );
+ </script>
+</head>
+<body></body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-003.html b/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-003.html
new file mode 100644
index 0000000000..5eb9448f67
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-003.html
@@ -0,0 +1,34 @@
+<!doctype html>
+<title>{Element,Range}.prototype.getBoundingClientRect on SVG &lt;tspan&gt;</title>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-range-getboundingclientrect">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<svg>
+ <text y="180" font-size="100" font-family="Ahem"
+ fill="lightblue">X<tspan fill="blue">XX</tspan></text>
+</svg>
+<script>
+ function check(object) {
+ let rect = object.getBoundingClientRect();
+ assert_equals(rect.left, 108, 'left');
+ assert_equals(rect.top, 108, 'top');
+ assert_equals(rect.width, 200, 'width');
+ assert_equals(rect.height, 100, 'height');
+ }
+ async_test(t => {
+ window.addEventListener("load", t.step_func_done(() => {
+ let tspan = document.querySelector('tspan');
+ check(tspan);
+ }));
+ }, document.title + ', Element');
+ async_test(t => {
+ window.addEventListener("load", t.step_func_done(() => {
+ let tspan = document.querySelector('tspan');
+ let range = new Range();
+ range.selectNode(tspan);
+ check(range);
+ }));
+ }, document.title + ', Range');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-vertical-rl-ref.html b/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-vertical-rl-ref.html
new file mode 100644
index 0000000000..0b68c4f210
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-vertical-rl-ref.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<body style="overflow: hidden">
+ <div style="position: absolute; top: 50px; left: 50px; width: 200px; height: 200px; background: green"></div>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-vertical-rl.html b/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-vertical-rl.html
new file mode 100644
index 0000000000..62f010afbf
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/cssom-getBoundingClientRect-vertical-rl.html
@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<title>CSSOM View - 6.1 - getBoundingClientRect tests</title>
+<link rel="help" href="http://www.w3.org/TR/cssom-view/#dom-element-getboundingclientrect">
+<link rel=match href="cssom-getBoundingClientRect-vertical-rl-ref.html">
+<meta name="flags" content="dom">
+<body style="writing-mode: vertical-rl; overflow: hidden">
+ <div id="target" style="position: absolute; top: 50px; left: 50px; width: 200px; height: 200px; background: red"></div>
+ <div id="overlay" style="position: absolute; background: green"></div>
+ <script>
+ var r = document.getElementById('target').getBoundingClientRect();
+ var overlay = document.getElementById('overlay');
+ overlay.style.top = r.top + 'px';
+ overlay.style.left = r.left + 'px';
+ overlay.style.width = r.width + 'px';
+ overlay.style.height = r.height + 'px';
+ </script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/cssom-getBoxQuads-001.html b/testing/web-platform/tests/css/cssom-view/cssom-getBoxQuads-001.html
new file mode 100644
index 0000000000..0ae204b9c0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/cssom-getBoxQuads-001.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM View - getBoxQuads() returns proper border and margin boxes for block and flex</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom-view/#the-geometryutils-interface">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+
+ <style>
+ .container {
+ width: 100px;
+ height: 50px;
+ background-color: gray;
+ }
+ span {
+ display: block;
+ background: gold;
+ height: 4px;
+ width: 14px;
+ margin: auto;
+ padding: 0px;
+ border: 3px solid blue;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="container">
+ <span id="block-block"></span>
+ </div>
+
+ <div class="container" style="display:flex">
+ <span id="flex-block"></span>
+ </div>
+
+ <script>
+ let bb = document.getElementById("block-block")
+ let fb = document.getElementById("flex-block")
+ test(function() {
+ assert_equals(bb.getBoxQuads({box: "border"})[0].getBounds().width, 20)
+ }, "Block layout border box is expected width.")
+ test(function() {
+ assert_equals(bb.getBoxQuads({box: "margin"})[0].getBounds().width, 100)
+ }, "Block layout margin box is expected width.")
+
+ // For containers that expand items to fill block-axis space, measure the box heights also.
+ test(function() {
+ assert_equals(fb.getBoxQuads({box: "border"})[0].getBounds().width, 20)
+ }, "Flex layout border box is expected width.")
+ test(function() {
+ assert_equals(fb.getBoxQuads({box: "margin"})[0].getBounds().width, 100)
+ }, "Flex layout margin box is expected width.")
+
+ test(function() {
+ assert_equals(fb.getBoxQuads({box: "border"})[0].getBounds().height, 10)
+ }, "Flex layout border box is expected height.")
+ test(function() {
+ assert_equals(fb.getBoxQuads({box: "margin"})[0].getBounds().height, 50)
+ }, "Flex layout margin box is expected height.")
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/cssom-getBoxQuads-002.html b/testing/web-platform/tests/css/cssom-view/cssom-getBoxQuads-002.html
new file mode 100644
index 0000000000..24c6a1935c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/cssom-getBoxQuads-002.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<title>CSSOM View - getBoxQuads() returns consistent box for SVG test</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#the-geometryutils-interface">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1746794">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<svg width="200" height="200">
+ <text>
+ <tspan id="t1" x="50 60 70 80 90 100" y="50">hello1</tspan>
+ <tspan id="t2" x="50 60 70 80 90 100" y="100">hello2</tspan>
+ </text>
+</svg>
+<script>
+ test(function() {
+ for (let element of document.querySelectorAll("tspan")) {
+ let bcr = element.getBoundingClientRect();
+ let quad = element.getBoxQuads()[0].getBounds();
+ for (let prop of ["width", "height", "left", "top"]) {
+ assert_equals(bcr[prop], quad[prop], `${element.id} ${prop}: getBoxQuads should be consistent with getBoundingClientRect`);
+ }
+ }
+ });
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/cssom-getClientRects-002.html b/testing/web-platform/tests/css/cssom-view/cssom-getClientRects-002.html
new file mode 100644
index 0000000000..9fd791d5db
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/cssom-getClientRects-002.html
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM View - GetClientRects().length is the same regardless source new lines</title>
+ <link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-element-getclientrects">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <span id="single">
+ test test
+ </span><br/>
+ <span id="multiple">
+ test
+ test
+ </span>
+ <script>
+ test(function () {
+ const count = document.querySelector("#single").getClientRects().length;
+ const count2 = document.querySelector("#multiple").getClientRects().length;
+ assert_equals(count, 1, "count");
+ assert_equals(count2, 1, "count2");
+ });
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/cssom-getClientRects.html b/testing/web-platform/tests/css/cssom-view/cssom-getClientRects.html
new file mode 100644
index 0000000000..36559eb116
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/cssom-getClientRects.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>CSS Test (CSSOM View): getClientRects of element outside DOM</title>
+ <link rel="author" title="Chris Rebert" href="http://chrisrebert.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#dom-element-getclientrects">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="Calling getClientRects on an element that is outside of the DOM (and therefore does not have an associated layout box) should result in an empty DOMRectList and should definitely not throw an error.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <script>
+ test(
+ function () {
+ var rectList = document.createElement('div').getClientRects();
+ assert_equals(rectList.length, 0, "DOMRectList should be empty");
+ },
+ "getClientRects on a newly-created Element not yet inserted into the DOM should return an empty DOMRectList"
+ );
+ </script>
+</head>
+<body></body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/cssom-view-img-attributes-001.html b/testing/web-platform/tests/css/cssom-view/cssom-view-img-attributes-001.html
new file mode 100644
index 0000000000..2f42288104
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/cssom-view-img-attributes-001.html
@@ -0,0 +1,46 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSS Test: HTMLImageElement x and y attributes</title>
+ <link rel="author" title="Adobe" href="http://html.adobe.com/">
+ <link rel="author" title="Alan Stearns" href="mailto:stearns@adobe.com">
+ <link rel="reviewer" title="" href="">
+ <link rel="help" href="https://drafts.csswg.org/cssom-view/#excensions-to-the-htmlimageelement-interface">
+ <meta name="assert" content="HTMLImageElement attributes give x and y position of CSS layout box">
+ <meta name="flags" content="dom">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="log"></div>
+ <script type="text/javascript">
+
+ function testIMGAttributes(attribute_name, display_none) {
+ var element = document.createElement("img");
+ document.body.appendChild(element);
+ element.style.setProperty("position", "absolute");
+ element.style.setProperty("left", "10px");
+ element.style.setProperty("top", "10px");
+ if (display_none) {
+ element.style.setProperty("display", "none");
+ }
+ var attributeValue = element[attribute_name];
+ document.body.removeChild(element);
+ return attributeValue;
+ }
+
+ var imgAttributes = [
+ ["x", false, 10],
+ ["y", false, 10],
+ ["x", true, 0],
+ ["y", true, 0],
+ ];
+
+ imgAttributes.forEach(function(test_data) {
+ test (function() { assert_equals(testIMGAttributes(test_data[0], test_data[1]), test_data[2])},
+ "test " + test_data[0] + " with display " + test_data[1])
+ }
+ );
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/cssom-view-window-screen-interface.html b/testing/web-platform/tests/css/cssom-view/cssom-view-window-screen-interface.html
new file mode 100644
index 0000000000..f4d3313002
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/cssom-view-window-screen-interface.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSS Test: cssom view window screen attribute</title>
+ <link rel="author" title="jingke" href="mailto:jingkewhu@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#the-screen-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="window screen interface">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ </head>
+ <body>
+ <div id="myDiv"></div>
+ <div id="log"></div>
+ <script>
+ test(function(){assert_true(window.screen.width >= 0 && window.screen.width < 6000000);},
+ "window.screen.width >= 0 && window.screen.width < 6000000");
+ test(function(){assert_true(window.screen.height >= 0 && window.screen.height < 6000000);},
+ "window.screen.height >= 0 && window.screen.height < 6000000");
+ test(function(){assert_true(window.screen.availWidth >= 0 && window.screen.availWidth <= window.screen.width);},
+ "window.screen.availWidth >= 0 && window.screen.availWidth <= window.screen.width");
+ test(function(){assert_true(window.screen.availHeight >= 0 && window.screen.availHeight <= window.screen.height);},
+ "window.screen.availHeight >= 0 && window.screen.availHeight <= window.screen.height");
+ test(function(){assert_in_array(window.screen.colorDepth, [0, 16, 24, 30, 32]);},
+ "window.screen.colorDepth == 0 || window.screen.colorDepth == 16 || window.screen.colorDepth == 24 || window.screen.colorDepth == 30 || window.screen.colorDepth == 32");
+ test(function(){assert_equals(window.screen.pixelDepth, window.screen.colorDepth);},
+ "window.screen.pixelDepth must return the value returned by window.screen.colorDepth");
+ </script>
+
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/devicePixelRatio-undisplayed-iframe.tentative.html b/testing/web-platform/tests/css/cssom-view/devicePixelRatio-undisplayed-iframe.tentative.html
new file mode 100644
index 0000000000..c3ee5c5133
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/devicePixelRatio-undisplayed-iframe.tentative.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-window-devicepixelratio">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1602347">
+<link rel="author" href="mailto:emilio@crisal.io" title="Emilio Cobos Álvarez">
+<link rel="author" href="https://mozilla.org" title="Mozilla">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<title>window.devicePixelRatio in an undisplayed iframe matches the dpi of the parent document</title>
+<div style="display: none">
+ <iframe src="about:blank"></iframe>
+</div>
+<script>
+onload = function() {
+ test(function() {
+ let win = document.querySelector("iframe").contentWindow;
+ assert_equals(win.devicePixelRatio, window.devicePixelRatio, "window.devicePixelRatio in an undisplayed iframe matches the dpi of the parent document");
+ });
+};
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/dom-element-scroll.html b/testing/web-platform/tests/css/cssom-view/dom-element-scroll.html
new file mode 100644
index 0000000000..3f3e27aaf6
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/dom-element-scroll.html
@@ -0,0 +1,100 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>dom-element-scroll tests</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrolltop">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #section1 {
+ width: 300px;
+ height: 500px;
+ top: 16px;
+ left: 16px;
+ border: inset gray 3px;
+ background: white;
+ }
+
+ #scrollable {
+ width: 400px;
+ height: 700px;
+ background: linear-gradient(135deg, red, blue);
+ }
+
+ #section2 {
+ width: 300px;
+ height: 500px;
+ top: 16px;
+ left: 16px;
+ border: inset gray 3px;
+ background: white;
+ }
+
+ #unscrollable {
+ width: 200px;
+ height: 300px;
+ background: linear-gradient(135deg, red, blue);
+ }
+</style>
+<section id="section1">
+ <div id="scrollable"></div>
+</section>
+<section id="section2">
+ <div id="unscrollable"></div>
+</section>
+<script>
+ var section1 = document.getElementById("section1");
+ var section2 = document.getElementById("section2");
+
+ test(function () {
+ // let it be "hidden" to have scrolling box
+ section1.style.overflow = "hidden";
+
+ section1.scroll(50, 60);
+ assert_equals(section1.scrollLeft, 50, "changed scrollLeft should be 50");
+ assert_equals(section1.scrollTop, 60, "changed scrollTop should be 60");
+
+ section1.scroll(0, 0); // back to the origin
+ }, "Element test for having scrolling box");
+
+ test(function () {
+ section1.scroll(10, 20);
+ assert_equals(section1.scrollLeft, 10, "changed scrollLeft should be 10");
+ assert_equals(section1.scrollTop, 20, "changed scrollTop should be 20");
+
+ section1.scroll(0, 0); // back to the origin
+ }, "Element test for having overflow");
+
+ test(function () {
+ // make it not "hidden" to not have scrolling box
+ section1.style.overflow = "visible";
+
+ section1.scroll(50, 0);
+ assert_equals(section1.scrollLeft, 0, "changed scrollLeft should be 0");
+ assert_equals(section1.scrollTop, 0, "changed scrollTop should be 0");
+
+ section1.scroll(0, 60);
+ assert_equals(section1.scrollLeft, 0, "changed scrollLeft should be 0");
+ assert_equals(section1.scrollTop, 0, "changed scrollTop should be 0");
+
+ section1.scroll(50, 60);
+ assert_equals(section1.scrollLeft, 0, "changed scrollLeft should be 0");
+ assert_equals(section1.scrollTop, 0, "changed scrollTop should be 0");
+
+ section1.scroll(0, 0); // back to the origin
+ }, "Element test for not having scrolling box");
+
+ test(function () {
+ section2.scroll(0, 20);
+ assert_equals(section2.scrollLeft, 0, "changed scrollLeft should be 0");
+ assert_equals(section2.scrollTop, 0, "changed scrollTop should be 0");
+
+ section2.scroll(10, 0);
+ assert_equals(section2.scrollLeft, 0, "changed scrollLeft should be 0");
+ assert_equals(section2.scrollTop, 0, "changed scrollTop should be 0");
+
+ section2.scroll(10, 20);
+ assert_equals(section2.scrollLeft, 0, "changed scrollLeft should be 0");
+ assert_equals(section2.scrollTop, 0, "changed scrollTop should be 0");
+ }, "Element test for not having overflow");
+
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-001.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-001.html
new file mode 100644
index 0000000000..c2fcc42c53
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-001.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM View - 5 - extensions to the Document interface</title>
+ <link rel="author" title="Neils Christoffersen" href="mailto:neils.christoffersen@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#extensions-to-the-document-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="elementFromPoint returns correct element">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ #targetDiv {
+ position: absolute;
+ top: 10;
+ left: 10;
+ height: 100px;
+ width: 100px;
+ }
+ </style>
+</head>
+<body>
+ <div id="myDiv"></div>
+ <div id="log"></div>
+ <div id="targetDiv">
+ </div>
+ <script>
+ var element = document.elementFromPoint(15, 15);
+
+ test ( function() {
+ assert_equals(element.id, "targetDiv", "elementFromPoint didn't return the correct element");
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-002.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-002.html
new file mode 100644
index 0000000000..ebab52fead
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-002.html
@@ -0,0 +1,40 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Checking whether dynamic changes to visibility interact correctly with
+ table anonymous boxes</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#overlay {
+ display: table;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: white;
+ z-index: 999
+}
+
+#wrapper { position: relative; }
+</style>
+<div id=log></div>
+<div id="wrapper">
+ <div id="overlay"><div></div></div>
+ <div id="target">Some text</div>
+</div>
+<script>
+ test(function() {
+ var t = document.querySelector("#target");
+ var rect = t.getBoundingClientRect();
+ var hit = document.elementFromPoint(rect.x + rect.width/2,
+ rect.y + rect.height/2);
+ assert_equals(hit, t.previousElementSibling,
+ "Should hit the overlay first.");
+ t.previousElementSibling.style.visibility = "hidden";
+ hit = document.elementFromPoint(rect.x + rect.width/2,
+ rect.y + rect.height/2);
+ assert_equals(hit, t,
+ "Should hit our target now that the overlay is hidden.");
+ });
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-003.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-003.html
new file mode 100644
index 0000000000..0a1ac40e72
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-003.html
@@ -0,0 +1,48 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Checking whether dynamic changes to visibility interact correctly with
+ table anonymous boxes</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+#overlay {
+ display: table;
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: white;
+ z-index: 999
+}
+
+#wrapper { position: relative; }
+</style>
+<div id=log></div>
+<div id="wrapper">
+ <div id="overlay"><div></div></div>
+ <div id="target">Some text</div>
+</div>
+<script>
+ test(function() {
+ // Make sure we have boxes constructed already.
+ document.body.offsetWidth;
+ var overlay = document.querySelector("#overlay");
+ overlay.insertBefore(document.createElement("div"), overlay.firstChild);
+ overlay.appendChild(document.createElement("div"));
+ // Make sure we have boxes constructed for those inserts/appends
+ document.body.offsetWidth;
+ overlay.firstChild.nextSibling.remove();
+ var t = document.querySelector("#target");
+ var rect = t.getBoundingClientRect();
+ var hit = document.elementFromPoint(rect.x + rect.width/2,
+ rect.y + rect.height/2);
+ assert_equals(hit, t.previousElementSibling,
+ "Should hit the overlay first.");
+ t.previousElementSibling.style.visibility = "hidden";
+ hit = document.elementFromPoint(rect.x + rect.width/2,
+ rect.y + rect.height/2);
+ assert_equals(hit, t,
+ "Should hit our target now that the overlay is hidden.");
+ });
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-dynamic-anon-box.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-dynamic-anon-box.html
new file mode 100644
index 0000000000..e0acb90fb8
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-dynamic-anon-box.html
@@ -0,0 +1,44 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>CSS Test: Hit testing on element previously hidden by an anonymous scroll box</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1433591">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+html, body {
+ margin: 0;
+ padding: 0;
+}
+/*
+ Create a hidden scrollbox that occupies the whole viewport, then give it
+ visibility: hidden dynamically. The link previously under the scrollbox
+ should be clickable.
+ */
+.scrollable {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100vw;
+ height: 100vh;
+ overflow: scroll;
+}
+
+.scrollable .inner {
+ display: table-cell;
+ width: 100vw;
+ height: 100vh;
+}
+</style>
+<div class="scrollable">
+ <div class="inner"></div>
+</div>
+<a href="#">Should be clickable</a>
+<script>
+test(function() {
+ assert_equals(document.elementFromPoint(10, 10).tagName, "DIV",
+ "Should hit the scrollbox contents");
+ document.querySelector('div').style.visibility = "hidden";
+ assert_equals(document.elementFromPoint(10, 10), document.querySelector('a'));
+}, "Link should be clickable after hiding a scrollbox with an anonymous table inside");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-ellipsis-in-inline-box.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-ellipsis-in-inline-box.html
new file mode 100644
index 0000000000..1e49e612eb
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-ellipsis-in-inline-box.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint" />
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.ellipsis {
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+</style>
+</head>
+<body>
+<div style="width: 100px">
+ <div>
+ <span><span class="item" style="display: inline-block">XXXXXXXXXXXXXXXXXXXX</span></span>
+ </div>
+ <div class="ellipsis">
+ <span><span class="item" style="display: inline-block">XXXXXXXXXXXXXXXXXXXX</span></span>
+ </div>
+ <div class="ellipsis">
+ <span><span class="item" style="display: inline-flex;">XXXXXXXXXXXXXXXXXXXX</span></span>
+ </div>
+</div>
+<div id="log"></div>
+<script>
+for (const item of document.getElementsByClassName('item')) {
+ test(() => {
+ const bounds = item.getBoundingClientRect();
+ const result = document.elementFromPoint(bounds.x + 10, bounds.y + bounds.height / 2);
+ assert_equals(result, item);
+ });
+}
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-float-in-relative.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-float-in-relative.html
new file mode 100644
index 0000000000..275fe29fb7
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-float-in-relative.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint" />
+<link rel="help" href="https://crbug.com/1146797">
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+ <div>
+ <span style="position: relative">
+ <span>
+ <a id="target" href="#" style="float: left">link</a>
+ </span>
+ </span>
+ </div>
+<script>
+test(() => {
+ let target = document.getElementById('target');
+ let bounds = target.getBoundingClientRect();
+ let result = document.elementFromPoint(bounds.left + 1, bounds.top + 1);
+ assert_equals(result, target);
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-float-in-table.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-float-in-table.html
new file mode 100644
index 0000000000..73713382ea
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-float-in-table.html
@@ -0,0 +1,60 @@
+<!DOCTYPE html>
+<link rel="help" href="http://www.w3.org/TR/CSS21/visuren.html#floats" />
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint" />
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+.outer {
+ width: 100px;
+ height: 100px;
+ background: green;
+}
+.inner {
+ float: left;
+ width: 50px;
+ height: 50px;
+ background: blue;
+}
+</style>
+<body>
+<div id="log"></div>
+
+<div class="outer" onclick="clicked('outer')">
+ <div id="float-in-div" class="inner" onclick="clicked('inner')">
+ </div>
+</div>
+
+<table>
+ <tr>
+ <td>
+ <div class="outer" onclick="clicked('outer')">
+ <div id='float-in-table' class="inner" onclick="clicked('inner')">
+ </div>
+ </div>
+ </td>
+ </tr>
+</table>
+
+<pre id="output"></pre>
+<script>
+function clicked(p) {
+ output.textContent += 'clicked ' + p + '\n';
+}
+
+function testElementFromPoint(element) {
+ let bounds = element.getBoundingClientRect();
+ let result = document.elementFromPoint(bounds.x + 1, bounds.y + 1);
+ assert_equals(result, element);
+}
+
+let targets = [
+ 'float-in-div',
+ 'float-in-table',
+];
+for (let target of targets) {
+ let element = document.getElementById(target);
+ test(() => { testElementFromPoint(element); }, target);
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-list-001.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-list-001.html
new file mode 100644
index 0000000000..b31453d6c3
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-list-001.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html>
+<title>Test elementFromPoint for list-item</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-document-elementfrompoint">
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ul {
+ font-size: 10px;
+}
+ul.inside {
+ list-style-position: inside;
+}
+.image {
+ list-style-image: url(../../images/green-16x16.png);
+}
+.debug-marker {
+ position: absolute;
+ width: 0;
+ height: 0;
+ outline: 2px solid red;
+}
+</style>
+<body>
+ <ul>
+ <li>Outside 1</li>
+ <li>Outside 2</li>
+ <li>Outside 3</li>
+ <li class="image">Image Outside 1</li>
+ <li class="image">Image Outside 2</li>
+ </ul>
+ <ul class="inside">
+ <li>Inside 1</li>
+ <li>Inside 2</li>
+ <li>Inside 3</li>
+ <li class="image">Image Inside 1</li>
+ <li class="image">Image Inside 2</li>
+ </ul>
+<script>
+setup({explicit_done:true});
+window.onload = function() {
+ for (let li of document.getElementsByTagName('li')) {
+ test(() => {
+ let bounds = li.getBoundingClientRect();
+ let style = window.getComputedStyle(li);
+ let y = (bounds.top + bounds.bottom) / 2;
+ if (style.listStylePosition === 'inside') {
+ // Inside markers are normal inline boxes.
+ // It is safe to assume "left + 1" is in the box.
+ let x = bounds.left + 1;
+ addDebugMarker(x, y);
+ let result = document.elementFromPoint(x, y);
+ assert_equals(result, li);
+ } else {
+ // The spec does not define outside marker position.
+ // This code assumes that the marker is within "left - 40" to "left - 1".
+ // This is heuristic, but all browsers seem to pass with this method.
+ let result = null;
+ for (let x = bounds.left - 40; x < bounds.left; x++) {
+ result = document.elementFromPoint(x, y);
+ if (result === li) {
+ addDebugMarker(x, y);
+ break;
+ }
+ }
+ assert_equals(result, li);
+ }
+ }, `<li>${li.textContent}</li>`);
+ }
+ done();
+};
+
+// Show a marker for the debugging purposes, in case the heuristic doesn't apply.
+function addDebugMarker(x, y) {
+ let div = document.createElement('div');
+ div.className = 'debug-marker';
+ let style = div.style;
+ style.left = x + 'px';
+ style.top = y + 'px';
+ document.body.appendChild(div);
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-mixed-font-sizes.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-mixed-font-sizes.html
new file mode 100644
index 0000000000..e2bac90afd
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-mixed-font-sizes.html
@@ -0,0 +1,23 @@
+<!DOCTYPE html>
+<link rel="help" href="http://www.w3.org/TR/cssom-view/#extensions-to-the-document-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div style="font-size: 40px">
+ <span id="target">
+ XXX <span id="small" style="font-size:10px">small</span> YYY
+ </span>
+</div>
+<script>
+test(() => {
+ // Find a point in the empty region above the "small" span, but still inside
+ // the "target" span.
+ const small = document.getElementById('small');
+ const rect = small.getBoundingClientRect();
+ const x = rect.left + rect.width / 2;
+ const y = rect.top - 5;
+
+ const actual = document.elementFromPoint(x, y);
+ const target = document.getElementById('target');
+ assert_equals(actual, target);
+}, 'document.elementFromPoint finds container SPAN in the empty region above a child SPAN with a smaller font size');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-parameters.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-parameters.html
new file mode 100644
index 0000000000..35afec994e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-parameters.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+<title>Tests that the parameters to document.elementFromPoint() and
+document.elementsFromPoint() are mandatory and of type double.</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extensions-to-the-document-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+function validate_function_parameter_count(testFunc, funcName) {
+ test(function() {
+ assert_throws_js(TypeError, function() {
+ testFunc();
+ }, "Called with no parameter");
+ assert_throws_js(TypeError, function() {
+ testFunc(0);
+ }, "Called with 1 parameter");
+ }, funcName + ": Parameters are mandatory.");
+}
+
+function validate_function_parameter_type(testFunc, funcName) {
+ test(function() {
+ assert_throws_js(TypeError, function() {
+ testFunc(0, Infinity);
+ }, "Passing Infinity as second parameter throws");
+ assert_throws_js(TypeError, function() {
+ testFunc(Infinity, 0);
+ }, "Passing Infinity as first parameter throws");
+ assert_throws_js(TypeError, function() {
+ testFunc(0, NaN);
+ }, "Passing NaN as second parameter throws");
+ assert_throws_js(TypeError, function() {
+ testFunc(NaN, 0);
+ }, "Passing NaN as first parameter throws");
+ }, funcName + ": Parameters should be finite floating point values.");
+}
+
+validate_function_parameter_count(function(x, y) {
+ document.elementFromPoint(x, y);
+}, "document.elementFromPoint");
+
+validate_function_parameter_type(function(x, y) {
+ document.elementFromPoint(x, y);
+}, "document.elementFromPoint");
+
+validate_function_parameter_count(function(x, y) {
+ document.elementsFromPoint(x, y);
+}, "document.elementsFromPoint");
+
+validate_function_parameter_type(function(x, y) {
+ document.elementsFromPoint(x, y);
+}, "document.elementsFromPoint");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-subpixel.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-subpixel.html
new file mode 100644
index 0000000000..ff67aa2e7f
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-subpixel.html
@@ -0,0 +1,59 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM View - extensions to the Document interface</title>
+ <link rel="author" title="Xiaocheng Hu" href="mailto:xiaochengh@chromium.org">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#extensions-to-the-document-interface">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+ <style>
+ .container {
+ display: flex;
+ width: 500px;
+ height: 100px;
+ }
+
+ .map {
+ flex: 1 1 auto;
+ position: relative;
+ }
+
+ .box {
+ flex: 0 0 auto;
+ }
+
+ .child {
+ width: 183.66px;
+ }
+ </style>
+</head>
+<body>
+ <div class="container">
+ <div class="map"></div>
+ <div class="box" id="box">
+ <div class="child"></div>
+ </div>
+ </div>
+
+ <script>
+ const box = document.getElementById('box');
+ const rect = box.getBoundingClientRect();
+
+ test(() => {
+ assert_equals(document.elementFromPoint(rect.x, rect.y), box);
+ }, 'Hit test top left corner of box');
+
+ test(() => {
+ assert_equals(document.elementFromPoint(rect.x + rect.width - 1, rect.y), box);
+ }, 'Hit test top right corner of box');
+
+ test(() => {
+ assert_equals(document.elementFromPoint(rect.x, rect.y + rect.height - 1), box);
+ }, 'Hit test bottom left corner of box');
+
+ test(() => {
+ assert_equals(document.elementFromPoint(rect.x + rect.width - 1, rect.y + rect.height - 1), box);
+ }, 'Hit test lower left corner of box');
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint-visibility-hidden-resizer.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint-visibility-hidden-resizer.html
new file mode 100644
index 0000000000..20b49e3e6a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint-visibility-hidden-resizer.html
@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<title>elementFromPoint on resizer area of an element with visibility:hidden</title>
+<link rel="help" href="http://www.w3.org/TR/cssom-view/#extensions-to-the-document-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="expected" style="position: absolute; top: 0; left: 0; width: 100px; height: 100px; background: blue"></div>
+<div id="hidden" style="position: absolute; top: 0; left: 0; width: 100px; height: 100px; visibility: hidden; overflow: scroll; resize: both"></div>
+<script>
+test(() => {
+ assert_equals(document.elementFromPoint(99, 99).id, "expected", "elementFromPoint should not return the hidden element");
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPoint.html b/testing/web-platform/tests/css/cssom-view/elementFromPoint.html
new file mode 100644
index 0000000000..e7f00bc575
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPoint.html
@@ -0,0 +1,208 @@
+<!DOCTYPE html>
+<title>cssom-view - elementFromPoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ .size {
+ width:60px;
+ height:60px;
+ }
+ .overlay {
+ position:absolute;
+ top:69px;
+ pointer-events:none;
+ }
+ .purple {
+ background-color: rebeccapurple;
+ }
+ .yellow {
+ background-color: yellow;
+ }
+ .teal {
+ background-color: teal;
+ }
+ .pink {
+ background-color: pink;
+ }
+</style>
+<body>
+ <div id='purple' class="size purple" >&nbsp;</div>
+ <div id='yellow' class="size yellow">&nbsp;</div>
+ <div id='teal' class="size overlay teal">&nbsp;</div>
+ <iframe id=iframe-1 src="iframe.html" style='display:none;position:absolute; left:300px;'></iframe>
+ <iframe id=iframe-2 src="iframe.html" width="" height=""></iframe>
+ <iframe id=iframe-3 width="" height=""></iframe>
+ <svg id=squiggle xmlns="http://www.w3.org/2000/svg" height="98" width="500" viewBox="0 0 581 98">
+ <path stroke-dashoffset="0.00" stroke-dasharray="" d="M62.9 14.9c-25-7.74-56.6 4.8-60.4 24.3-3.73 19.6 21.6 35 39.6 37.6 42.8 6.2 72.9-53.4 116-58.9 65-18.2 191 101 215 28.8 5-16.7-7-49.1-34-44-34 11.5-31 46.5-14 69.3 9.38 12.6 24.2 20.6 39.8 22.9 91.4 9.05 102-98.9 176-86.7 18.8 3.81 33 17.3 36.7 34.6 2.01 10.2.124 21.1-5.18 30.1" stroke="#000" stroke-width="4.3" fill="none">
+ </path>
+ </svg>
+ <svg id=svg-transform width="180" height="140"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+
+ <!-- Now we add a text element and apply rotate and translate to both -->
+ <rect x="50" y="50" height="60" width="60" style="stroke:#000; fill: #0086B2" transform="translate(30) rotate(45 50 50)"></rect>
+ <text x="60" y="105" transform="translate(30) rotate(45 50 50)"> Hello! </text>
+
+</svg>
+ <div id='pink' class='size pink' style='transform: translate(10px)'>&nbsp;</div>
+ <div id='anotherteal' class='size teal' style='pointer-events:none'>Another teal</div>
+ <img id="dinos" src="/images/blue-area.png" usemap="#dinos_map" border="0" width="364" height="40"/>
+ <map id="dinos_map" name="dinos_map">
+ <area id="rectG" shape="rect" coords="0,0,90,100" href="#" alt="area 1"/>
+ <area id="circleO" shape="circle" coords="120,60,30" href="#" alt="area 2"/>
+ <area id="polyLE" shape="poly" coords="280,0,310,0,360,30,360,90,280,90" href="#" alt="area 3"/>
+ </map>
+ <!-- Test for fieldsets not doing weird things. Use a 200x200 div to hold
+ all the bits for this test. Also, place it top/right, so it is not below
+ the bottom edge of the viewport. -->
+ <div style="position: absolute; width: 200px; height: 200px; right: 0; top: 0">
+ <div id="fieldset-div"
+ class="size" style="position: absolute; top: 0; left: 0">
+ </div>
+ <fieldset id="fieldset"
+ class="size"
+ style="position: absolute; top: 100px; left: 100px; border-radius: 100px">
+ <!-- Place the child span so the overflow area of the fieldset overlaps
+ the div -->
+ <span style="position: absolute; top: -100px; left: -100px; height: 1px; width: 1px"></span>
+ </fieldset>
+ </div>
+ <script>
+ setup({explicit_done:true});
+ window.onload = function () {
+ test(function () {
+ assert_equals(document.elementFromPoint(-1, -1), null,
+ "both co-ordinates passed in are negative so should have returned a null");
+ assert_equals(document.elementFromPoint(-1, -1), null,
+ "x co-ordinates passed in are negative so should have returned a null");
+ assert_equals(document.elementFromPoint(-1, -1), null,
+ "y co-ordinates passed in are negative so should have returned a null");
+ }, "Negative co-ordinates");
+
+ test(function () {
+ var viewportX = window.innerWidth;
+ var viewportY = window.innerHeight;
+ assert_equals(document.elementFromPoint(viewportX + 100, 10), null,
+ "X co-ordinates larger than viewport");
+ assert_equals(document.elementFromPoint(10, viewportY + 100), null,
+ "Y co-ordinates larger than viewport");
+ assert_equals(document.elementFromPoint(viewportX + 100, viewportY + 100), null,
+ "X, Y co-ordinates larger than viewport");
+ }, "co-ordinates larger than the viewport");
+
+ test(function () {
+ var viewportX = window.frames[1].innerWidth;
+ var viewportY = window.frames[1].innerHeight;
+ var iframeRect = document.getElementById('iframe-2').getBoundingClientRect();
+ assert_equals(null, window.frames[1].document.elementFromPoint(iframeRect.right + viewportX + 100, 10),
+ "X co-ordinates larger than viewport");
+ assert_equals(null, window.frames[1].document.elementFromPoint(10, iframeRect.bottom + viewportY + 100),
+ "Y co-ordinates larger than viewport");
+ assert_equals(null, window.frames[1].document.elementFromPoint(iframeRect.right + viewportX + 100,
+ iframeRect.bottom + viewportY + 100),
+ "X, Y co-ordinates larger than viewport");
+ }, "co-ordinates larger than the viewport from in iframe");
+
+ test(function () {
+ assert_equals(document.elementFromPoint(10, 10), document.getElementById('purple'),
+ "Should have returned the element with id `purple`");
+ }, "Return first element that is the target for hit testing");
+
+ test(function () {
+ assert_equals(document.elementFromPoint(10, 80), document.getElementById('yellow'),
+ "Should have returned the element with id `yellow` as element with `teal` has `pointer-events:none`");
+ }, "First element to get mouse events with pointer-events css");
+
+ test(function () {
+ var svg = document.getElementById('squiggle');
+ svg.scrollIntoView();
+ var svgRect = svg.getBoundingClientRect();
+ assert_equals(document.elementFromPoint(svgRect.left + Math.round(svgRect.width/2),
+ svgRect.top + Math.round(svgRect.height/2)),
+ svg,
+ "Should have returned the line SVG");
+ }, "SVG element at x,y");
+
+ test(function () {
+ var svg = document.getElementById('svg-transform');
+ svg.scrollIntoView();
+ var svgRect = svg.getBoundingClientRect();
+ assert_equals(document.elementFromPoint(svgRect.left + Math.round(svgRect.width/2),
+ svgRect.top + Math.round(svgRect.height/2)),
+ svg.querySelector("rect"),
+ "Should have returned SVG with Hello WPT! that has a transform");
+
+ var pink = document.getElementById('pink');
+ pink.scrollIntoView();
+ var pinkRect = pink.getBoundingClientRect();
+ assert_equals(document.elementFromPoint(pinkRect.left + Math.round(pinkRect.width/2),
+ pinkRect.top + Math.round(pinkRect.height/2)),
+ pink,
+ "Should have returned pink element that has a transform");
+
+ }, "transformed element at x,y");
+
+ test(function () {
+ var anotherteal = document.getElementById('anotherteal');
+ anotherteal.scrollIntoView();
+ var anothertealRect = anotherteal.getBoundingClientRect();
+ assert_equals(document.elementFromPoint(anothertealRect.left + Math.round(anothertealRect.width/2),
+ anothertealRect.top + Math.round(anothertealRect.height/2)),
+ document.body,
+ "Should have returned the root as it has pointer-events:none");
+
+ var doc = frames[2].document;
+ doc.removeChild(doc.documentElement);
+ assert_equals(doc.elementFromPoint(0, 0), null,
+ "Should have returned null as no root element");
+ }, "no hit target at x,y");
+
+ test(function () {
+ var doc = document.implementation.createHTMLDocument('foo');
+ assert_equals(doc.elementFromPoint(0, 0), null,
+ "Should have returned the documentElement for the document")
+ }, "No viewport available");
+
+ test(function () {
+ // HTML says:
+ // Pointing device interaction with an image associated with a set of layered shapes per
+ // the above algorithm must result in the relevant user interaction events being first
+ // fired to the top-most shape covering the point that the pointing device indicated, if
+ // any, or to the image element itself, if there is no shape covering that point.
+ // https://html.spec.whatwg.org/multipage/embedded-content.html#image-map-processing-model
+ var area = document.getElementById('rectG');
+ var image = document.getElementById('dinos');
+ area.scrollIntoView();
+ var areaRect = image.getBoundingClientRect();
+ areaRect.width = Math.min(areaRect.width, 90);
+ areaRect.height = Math.min(areaRect.height, 100);
+ assert_equals(document.elementFromPoint(areaRect.left + Math.round(areaRect.width/2),
+ areaRect.top + Math.round(areaRect.height/2)),
+ area,
+ "Should have returned the area element");
+ assert_equals(document.elementFromPoint(areaRect.left + 92,
+ areaRect.top + 2),
+ image,
+ "Should have returned the image element");
+ }, "Image Maps");
+
+ test(function(){
+ var fieldsetDiv = document.getElementById("fieldset-div");
+ var divRect = fieldsetDiv.getBoundingClientRect();
+ assert_equals(document.elementFromPoint(divRect.left + divRect.width/2,
+ divRect.top + divRect.height/2),
+ fieldsetDiv,
+ "The fieldset should not cover up the div it doesn't even overlap");
+
+ var fieldset = document.getElementById("fieldset");
+ var rect = fieldset.getBoundingClientRect();
+ // A point 5px in from topleft will be outside the rounded border.
+ assert_not_equals(document.elementFromPoint(rect.left + 5,
+ rect.top + 5),
+ fieldset,
+ "The fieldset should not be hit by hit-tests outside its rounded border");
+ }, "Fieldsets");
+ done();
+ }
+ </script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementFromPosition.html b/testing/web-platform/tests/css/cssom-view/elementFromPosition.html
new file mode 100644
index 0000000000..e7a7b4afee
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementFromPosition.html
@@ -0,0 +1,121 @@
+<!DOCTYPE HTML>
+<html lang="en-US">
+<head>
+ <title>CSS Test: CSSOM View elementFromPoint</title>
+ <meta charset="UTF-8">
+ <link rel="author" title="Chris" href="mailto:pwx.frontend@gmail.com" />
+ <link rel="help" href="https://www.w3.org/TR/cssom-view/#dom-document-elementfrompoint" />
+ <meta name="flags" content="dom" />
+ <script src="/resources/testharness.js" type="text/javascript"></script>
+ <script src="/resources/testharnessreport.js" type="text/javascript"></script>
+</head>
+<body>
+ <noscript>Test not run - JavaScript required!</noscript>
+ <div id="log"></div>
+ <script type="text/javascript">
+
+ var body = document.getElementsByTagName( 'body' )[0];
+ function createElement( id ) {
+ var elem = document.createElement( 'div' );
+ if ( id && typeof id == 'string' ) {
+ elem.id = id;
+ }
+ body.appendChild( elem );
+ return elem;
+ }
+
+ function setPosition( config ) {
+ var target = config.target;
+ target.style.position = 'absolute';
+ target.style.left = config.left + 'px';
+ target.style.top = config.top + 'px';
+ target.style.width = config.width + 'px';
+ target.style.height = config.height + 'px';
+ if ( config['z-index'] ) {
+ target.style.zIndex = config['z-index'];
+ }
+ }
+
+ var target = createElement( 'dom-1' );
+ setPosition( {
+ width: 100,
+ height: 100,
+ left: 10,
+ top: 10,
+ target: target
+ });
+
+ test( function () {
+ assert_inherits( document, 'elementFromPoint' );
+ }, 'document.elementFromPoint');
+
+ test( function () {
+ assert_true( document.elementFromPoint instanceof Function );
+ }, 'document.elementFromPoint is a Function');
+ (function(){
+ var wrap = [
+ // 左上角.
+ {x: 10, y: 10, r: 'top left corner'},
+ // 上边线
+ {x: 50, y: 10, r: 'top line'},
+ // 右上角
+ {x: 110, y: 10, r: 'top right corner'},
+ // 左边线
+ {x: 10, y: 50, r: 'left line'},
+ // 元素范围内
+ {x: 50, y: 50, r: 'inside'},
+ // 右边线
+ {x: 110, y: 50, r: 'right line'},
+ // 左下角
+ {x: 10, y: 110, r: 'bottom left corner'},
+ // 下边线
+ {x: 50, y: 110, r: 'bottom line'},
+ // 右下角
+ {x: 110, y: 110, r: 'bottom right corner'}
+ ];
+ var i = 0, len = wrap.length, item;
+ for ( ; i < len; i++ ) {
+ item = wrap[ i ];
+ test( function () {
+ assert_equals( document.elementFromPoint( item.x, item.y).id == 'dom-1', true );
+ }, 'test some point of the element: ' + item.r);
+ }
+ })();
+ test( function () {
+ var elem = document.elementFromPoint( 0, 0 );
+ assert_equals( elem.nodeName, 'HTML' );
+ }, 'Point (0, 0), return root element(HTML)' );
+
+ test( function () {
+ var elem = document.elementFromPoint( -1000, 0 );
+ assert_equals( elem, null, 'negative x, return null' );
+ }, ' test negative x ');
+
+ test( function () {
+ var elem = document.elementFromPoint( 0, -1000 );
+ assert_equals( elem, null, 'negative y, return null' );
+ }, ' test negative y ');
+
+ test( function () {
+ var elem = document.elementFromPoint( 100000, 0 );
+ assert_equals( elem, null );
+ }, 'test outside of viewport');
+
+ test( function () {
+ var config = {
+ width: 100,
+ height: 100,
+ left: 5,
+ top: 5
+ };
+ var target2 = createElement( 'dom-2' );
+ config.target = target2;
+ setPosition( config );
+
+ var elem = document.elementFromPoint( 10, 10 );
+ var elem2 = document.elementFromPoint( 10, 10 );
+ assert_equals( elem.id, elem2.id );
+ }, 'test the top of layer');
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/elementScroll-002.html b/testing/web-platform/tests/css/cssom-view/elementScroll-002.html
new file mode 100644
index 0000000000..a1a28aab9e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementScroll-002.html
@@ -0,0 +1,57 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>cssom-view - elementScroll - 002</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrolltop">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollleft">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="scroller1" style="height: 100px; width: 100px; overflow: scroll; background: red;">
+ <div style="background: green; margin-top: 100px; margin-left: 100px; width: 100px; height: 100px;"></div>
+</div>
+<div id="scroller2" style="height: 100px; width: 100px; overflow: hidden; background: red;">
+ <div style="background: green; margin-top: 100px; padding-left: 100px; width: 100px; height: 100px;"></div>
+</div>
+<div id="scroller3" style="height: 100px; width: 100px; overflow: scroll; background: red;">
+ <div style="background: green; padding-top: 100px; margin-left: 100px; width: 100px; height: 100px;"></div>
+</div>
+<div id="scroller4" style="height: 100px; width: 100px; overflow: hidden; background: red;">
+ <div style="background: green; padding-top: 100px; padding-left: 100px; width: 100px; height: 100px;"></div>
+</div>
+<script>
+ test(function () {
+ var scroller1 = document.getElementById("scroller1");
+
+ scroller1.scrollTop = 100;
+ scroller1.scrollLeft = 100;
+ assert_equals(scroller1.scrollTop, 100, "changed scrollTop should be 100");
+ assert_equals(scroller1.scrollLeft, 100, "changed scrollLeft should be 100");
+
+ }, "simple scroll with style: 'margin' and 'overflow: scroll'");
+ test(function () {
+ var scroller2 = document.getElementById("scroller2");
+
+ scroller2.scrollTop = 100;
+ scroller2.scrollLeft = 100;
+ assert_equals(scroller2.scrollTop, 100, "changed scrollTop should be 100");
+ assert_equals(scroller2.scrollLeft, 100, "changed scrollLeft should be 100");
+
+ }, "simple scroll with style: 'margin' and 'overflow: hidden'");
+ test(function () {
+ var scroller3 = document.getElementById("scroller3");
+
+ scroller3.scrollTop = 100;
+ scroller3.scrollLeft = 100;
+ assert_equals(scroller3.scrollTop, 100, "changed scrollTop should be 100");
+ assert_equals(scroller3.scrollLeft, 100, "changed scrollLeft should be 100");
+
+ }, "simple scroll with style: 'padding' and 'overflow: scroll'");
+ test(function () {
+ var scroller4 = document.getElementById("scroller4");
+
+ scroller4.scrollTop = 100;
+ scroller4.scrollLeft = 100;
+ assert_equals(scroller4.scrollTop, 100, "changed scrollTop should be 100");
+ assert_equals(scroller4.scrollLeft, 100, "changed scrollLeft should be 100");
+
+ }, "simple scroll with style: 'padding' and 'overflow: hidden'");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementScroll.html b/testing/web-platform/tests/css/cssom-view/elementScroll.html
new file mode 100644
index 0000000000..2ef76e9d64
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementScroll.html
@@ -0,0 +1,173 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>cssom-view - elementScroll</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #section {
+ width: 300px;
+ height: 500px;
+ /*position: absolute;*/
+ top: 16px;
+ left: 16px;
+ border: inset gray 3px;
+ overflow: hidden;
+ background: white;
+ }
+
+ #scrollable {
+ width: 400px;
+ height: 700px;
+ background: linear-gradient(135deg, red, blue);
+ }
+
+</style>
+
+<section id="section">
+ <div id="scrollable"></div>
+ <div id="unrelated"></div>
+</section>
+
+<script>
+ setup({explicit_done:true});
+ window.onload = function () {
+ var section = document.getElementById("section");
+ var unrelated = document.getElementById("unrelated");
+
+ test(function () {
+ assert_equals(section.scrollTop, 0, "initial scrollTop should be 0");
+ assert_equals(section.scrollLeft, 0, "initial scrollLeft should be 0");
+
+ section.scrollTop = 30;
+ section.scrollLeft = 40;
+
+ assert_equals(section.scrollTop, 30, "changed scrollTop should be 40");
+ assert_equals(section.scrollLeft, 40, "changed scrollLeft should be 40");
+ assert_equals(unrelated.scrollTop, 0, "unrelated element should not scroll");
+ assert_equals(unrelated.scrollLeft, 0, "unrelated element should not scroll");
+ }, "Element scrollTop/Left getter/setter test");
+
+ test(function () {
+ section.scroll(50, 60);
+
+ assert_equals(section.scrollLeft, 50, "changed scrollLeft should be 50");
+ assert_equals(section.scrollTop, 60, "changed scrollTop should be 60");
+ }, "Element scroll test (two arguments)");
+
+ test(function () {
+ section.scroll({left: 55, top: 65});
+
+ assert_equals(section.scrollLeft, 55, "changed scrollLeft should be 55");
+ assert_equals(section.scrollTop, 65, "changed scrollTop should be 65");
+
+ section.scroll({left: 85});
+
+ assert_equals(section.scrollLeft, 85, "changed scrollLeft should be 85");
+ assert_equals(section.scrollTop, 65, "scrollTop should stay at 65");
+
+ section.scroll({top: 75});
+
+ assert_equals(section.scrollLeft, 85, "scrollLeft should stay at 85");
+ assert_equals(section.scrollTop, 75, "changed scrollTop should be 75");
+
+ section.scroll({});
+
+ assert_equals(section.scrollLeft, 85, "scrollLeft should stay at 85");
+ assert_equals(section.scrollTop, 75, "scrollTop should stay at 75");
+
+ section.scroll();
+
+ assert_equals(section.scrollLeft, 85, "scrollLeft should stay at 85");
+ assert_equals(section.scrollTop, 75, "scrollTop should stay at 75");
+ }, "Element scroll test (one argument)");
+
+ test(function () {
+ section.scrollTo(80, 70);
+
+ assert_equals(section.scrollLeft, 80, "changed scrollLeft should be 70");
+ assert_equals(section.scrollTop, 70, "changed scrollTop should be 80");
+ }, "Element scrollTo test (two arguments)");
+
+ test(function () {
+ section.scrollTo({left: 75, top: 85});
+
+ assert_equals(section.scrollLeft, 75, "changed scrollLeft should be 75");
+ assert_equals(section.scrollTop, 85, "changed scrollTop should be 85");
+
+ section.scrollTo({left: 65});
+
+ assert_equals(section.scrollLeft, 65, "changed scrollLeft should be 65");
+ assert_equals(section.scrollTop, 85, "scrollTop should stay at 85");
+
+ section.scrollTo({top: 55});
+
+ assert_equals(section.scrollLeft, 65, "scrollLeft should stay at 65");
+ assert_equals(section.scrollTop, 55, "changed scrollTop should be 55");
+
+ section.scrollTo({});
+
+ assert_equals(section.scrollLeft, 65, "scrollLeft should stay at 65");
+ assert_equals(section.scrollTop, 55, "scrollTop should stay at 55");
+
+ section.scrollTo();
+
+ assert_equals(section.scrollLeft, 65, "scrollLeft should stay at 55");
+ assert_equals(section.scrollTop, 55, "scrollTop should stay at 55");
+ }, "Element scrollTo test (one argument)");
+
+ test(function () {
+ var left = section.scrollLeft;
+ var top = section.scrollTop;
+
+ section.scrollBy(10, 20);
+
+ assert_equals(section.scrollLeft, left + 10, "increment of scrollLeft should be 10")
+ assert_equals(section.scrollTop, top + 20, "increment of scrollTop should be 20")
+ }, "Element scrollBy test (two arguments)");
+
+ test(function () {
+ var left = section.scrollLeft;
+ var top = section.scrollTop;
+
+ section.scrollBy({left: 5, top: 15});
+ left += 5
+ top += 15
+
+ assert_equals(section.scrollLeft, left, "increment of scrollLeft should be 5")
+ assert_equals(section.scrollTop, top, "increment of scrollTop should be 15")
+
+ section.scrollBy({left: -15});
+ left -= 15
+
+ assert_equals(section.scrollLeft, left, "decrement of scrollLeft should be 15")
+ assert_equals(section.scrollTop, top, "scrollTop should not be modified")
+
+ section.scrollBy({top: -5});
+ top -= 5;
+
+ assert_equals(section.scrollLeft, left, "scrollLeft should not be modified")
+ assert_equals(section.scrollTop, top, "decrement of scrollTop should be 5")
+
+ section.scrollBy({});
+
+ assert_equals(section.scrollLeft, left, "scrollLeft should not be modified")
+ assert_equals(section.scrollTop, top, "scrollTop should not be modified")
+
+ section.scrollBy();
+
+ assert_equals(section.scrollLeft, left, "scrollLeft should not be modified")
+ assert_equals(section.scrollTop, top, "scrollTop should not be modified")
+ }, "Element scrollBy test (one argument)");
+
+ test(function () {
+ section.scrollTop = 1000;
+ section.scrollLeft = 1000;
+
+ assert_equals(section.scrollTop, 700 - 500, "changed scrollTop should be 200");
+ assert_equals(section.scrollLeft, 400 - 300, "changed scrollLeft should be 100");
+
+ }, "Element scroll maximum test");
+
+ done();
+ };
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-iframes.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-iframes.html
new file mode 100644
index 0000000000..3bba161b47
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-iframes.html
@@ -0,0 +1,83 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<script>
+var loadedFrameCount = 0;
+var t1 = async_test('elementsFromPoint on the root document for points in iframe elements');
+var t2 = async_test('elementsFromPoint on inner documents');
+
+function onFrameLoaded() {
+ loadedFrameCount++;
+ if (loadedFrameCount < 2)
+ return;
+
+ var body = document.body;
+ var html = document.documentElement;
+ var iframe = document.getElementById('iframe');
+ var scrollableIframe = document.getElementById('scrollableIframe');
+ t1.step(function() {
+ checkElementsFromPointFourCorners('document', 'iframe',
+ [iframe, body, html],
+ [iframe, body, html],
+ [iframe, body, html],
+ [scrollableIframe, iframe, body, html]);
+
+ checkElementsFromPointFourCorners('document', 'scrollableIframe',
+ [scrollableIframe, iframe, body, html],
+ [scrollableIframe, iframe, body, html],
+ [scrollableIframe, iframe, body, html],
+ [scrollableIframe, iframe, body, html]);
+ });
+ t1.done();
+
+ t2.step(function() {
+ var iframeDocument = document.getElementById('iframe').contentDocument;
+ var iframeRoot = iframeDocument.documentElement;
+ var iframeBody = iframeDocument.body;
+ var iframeDiv = iframeDocument.getElementById('div');
+ checkElementsFromPointFourCorners('document.getElementById(\'iframe\').contentDocument', 'div',
+ [iframeDiv, iframeBody, iframeRoot],
+ [iframeDiv, iframeBody, iframeRoot],
+ [iframeDiv, iframeBody, iframeRoot],
+ [iframeDiv, iframeBody, iframeRoot]);
+
+ var iframeDocument2 = document.getElementById('scrollableIframe').contentDocument;
+ var iframeRoot2 = iframeDocument2.documentElement;
+ var iframeBody2 = iframeDocument2.body;
+ var iframeSmallDiv = iframeDocument2.getElementById('small');
+ var iframeBigDiv = iframeDocument2.getElementById('big');
+ checkElementsFromPointFourCorners('document.getElementById(\'scrollableIframe\').contentDocument', 'big',
+ [iframeSmallDiv, iframeBigDiv, iframeBody2, iframeRoot2],
+ [iframeBigDiv, iframeBody2, iframeRoot2],
+ [],
+ []);
+ });
+ t2.done();
+}
+</script>
+<style>
+html, body {
+ margin: 0;
+ padding: 0;
+}
+body {
+ height: 500px;
+}
+#iframe {
+ width: 200px;
+ height: 200px;
+}
+#scrollableIframe {
+ position: absolute;
+ top: 0;
+ left: 0;
+ transform: translate(50px, 50px);
+ width: 150px;
+ height: 150px;
+ overflow-y: scroll;
+ overflow-x: scroll;
+}
+</style>
+<iframe id="iframe" src="resources/iframe1.html"></iframe>
+<iframe id="scrollableIframe" src="resources/iframe2.html"></iframe>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-htb-ltr.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-htb-ltr.html
new file mode 100644
index 0000000000..5d6f92f3d1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-htb-ltr.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#extensions-to-the-document-interface">
+<div id="container" style="width:200px; height:200px; writing-mode:horizontal-tb; direction:ltr;">
+ <span id="target">target</span>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> {
+ var elements = document.elementsFromPoint(15, 15);
+ assert_equals(elements.length, 4);
+ assert_equals(elements[0].id, "target");
+ assert_equals(elements[1].id, "container");
+ assert_equals(elements[2].nodeName, "BODY");
+ assert_equals(elements[3].nodeName, "HTML");
+ }, "elementsFromPoint should return all elements under a point");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-htb-rtl.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-htb-rtl.html
new file mode 100644
index 0000000000..3ff7f01399
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-htb-rtl.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#extensions-to-the-document-interface">
+<div id="container" style="width:200px; height:200px; writing-mode:horizontal-tb; direction:rtl;">
+ <span id="target">target</span>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> {
+ var elements = document.elementsFromPoint(200, 15);
+ assert_equals(elements.length, 4);
+ assert_equals(elements[0].id, "target");
+ assert_equals(elements[1].id, "container");
+ assert_equals(elements[2].nodeName, "BODY");
+ assert_equals(elements[3].nodeName, "HTML");
+ }, "elementsFromPoint should return all elements under a point");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vlr-ltr.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vlr-ltr.html
new file mode 100644
index 0000000000..c22c005159
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vlr-ltr.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#extensions-to-the-document-interface">
+<div id="container" style="width:200px; height:200px; writing-mode:vertical-lr; direction:ltr;">
+ <span id="target">target</span>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> {
+ var elements = document.elementsFromPoint(15, 15);
+ assert_equals(elements.length, 4);
+ assert_equals(elements[0].id, "target");
+ assert_equals(elements[1].id, "container");
+ assert_equals(elements[2].nodeName, "BODY");
+ assert_equals(elements[3].nodeName, "HTML");
+ }, "elementsFromPoint should return all elements under a point");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vlr-rtl.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vlr-rtl.html
new file mode 100644
index 0000000000..e8e600a8b8
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vlr-rtl.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#extensions-to-the-document-interface">
+<div id="container" style="width:200px; height:200px; writing-mode:vertical-lr; direction:rtl;">
+ <span id="target">target</span>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> {
+ var elements = document.elementsFromPoint(15, 200);
+ assert_equals(elements.length, 4);
+ assert_equals(elements[0].id, "target");
+ assert_equals(elements[1].id, "container");
+ assert_equals(elements[2].nodeName, "BODY");
+ assert_equals(elements[3].nodeName, "HTML");
+ }, "elementsFromPoint should return all elements under a point");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vrl-ltr.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vrl-ltr.html
new file mode 100644
index 0000000000..598d41e41b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vrl-ltr.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#extensions-to-the-document-interface">
+<div id="container" style="width:200px; height:200px; writing-mode:vertical-rl; direction:ltr;">
+ <span id="target">target</span>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> {
+ var elements = document.elementsFromPoint(200, 15);
+ assert_equals(elements.length, 4);
+ assert_equals(elements[0].id, "target");
+ assert_equals(elements[1].id, "container");
+ assert_equals(elements[2].nodeName, "BODY");
+ assert_equals(elements[3].nodeName, "HTML");
+ }, "elementsFromPoint should return all elements under a point");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vrl-rtl.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vrl-rtl.html
new file mode 100644
index 0000000000..4d93bf5210
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-inline-vrl-rtl.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#extensions-to-the-document-interface">
+<div id="container" style="width:200px; height:200px; writing-mode:vertical-rl; direction:rtl;">
+ <span id="target">target</span>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=> {
+ var elements = document.elementsFromPoint(200, 200);
+ assert_equals(elements.length, 4);
+ assert_equals(elements[0].id, "target");
+ assert_equals(elements[1].id, "container");
+ assert_equals(elements[2].nodeName, "BODY");
+ assert_equals(elements[3].nodeName, "HTML");
+ }, "elementsFromPoint should return all elements under a point");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-invalid-cases.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-invalid-cases.html
new file mode 100644
index 0000000000..369cffcd31
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-invalid-cases.html
@@ -0,0 +1,56 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<style>
+html {
+ overflow-y: scroll;
+ overflow-x: scroll;
+}
+html, body {
+ margin: 0;
+ padding: 0;
+}
+body {
+ width: 100%;
+ height: 100%;
+}
+#simpleDiv {
+ width: 200px;
+ height: 200px;
+ background-color: rgba(0,255,0,0.5);
+}
+#beyondTopLeft {
+ position: absolute;
+ transform: translate3d(-100px, -100px, 10px);
+ left: 0;
+ top: 0;
+ width: 100px;
+ height: 100px;
+ background-color: rgba(0,0,0,0.1);
+}
+</style>
+<body>
+<div id="beyondTopLeft"></div>
+<div id="simpleDiv"></div>
+<script>
+test(function() {
+ assertElementsFromPoint('document', 300, 300, [document.documentElement]);
+}, "The root element is the last element returned for otherwise empty queries within the viewport");
+
+test(function() {
+ var simpleDiv = document.getElementById('simpleDiv');
+ var simpleRect = simpleDiv.getBoundingClientRect();
+ var simpleCoords = (simpleRect.right - 1) + ', ' + (simpleRect.bottom - 1);
+ assertElementsFromPoint('document', simpleRect.right - 1, simpleRect.bottom - 1,
+ [simpleDiv, document.body, document.documentElement]);
+}, "The root element is the last element returned for valid queries");
+
+test(function() {
+ assertElementsFromPoint('document', window.innerWidth + 1, window.innerHeight + 1, []);
+ assertElementsFromPoint('document', -1, -1, []);
+ assertElementsFromPoint('document', 1, -1, []);
+ assertElementsFromPoint('document', -1, 1, []);
+}, "An empty sequence is returned for queries outside the viewport");
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-shadowroot.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-shadowroot.html
new file mode 100644
index 0000000000..b3e0c6db26
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-shadowroot.html
@@ -0,0 +1,87 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<style>
+html, body {
+ margin: 0;
+ padding: 0;
+}
+body {
+ height: 500px;
+}
+</style>
+<body>
+<div id="host"></div>
+<div id="blockHost"></div>
+<span id="inlineBlockHost" style="display:inline-block;"></span>
+<input type="submit" id="submit">
+<script>
+function assertElementsFromPoint(doc, x, y, expected) {
+ var query = doc + '.elementsFromPoint(' + x + ',' + y + ')';
+ var sequence = eval(query);
+ assert_equals(nodeListToString(sequence), nodeListToString(expected), query);
+}
+
+function createBox(id) {
+ var div = document.createElement('div');
+ div.id = id;
+ div.style.width = '100px';
+ div.style.height = '10px';
+ return div;
+}
+
+function centerX(element) {
+ return element.offsetLeft + element.offsetWidth / 2;
+}
+
+function centerY(element) {
+ return element.offsetTop + element.offsetHeight / 2;
+}
+
+var shadowRoot = host.attachShadow({mode: 'closed'});
+var box11 = createBox('box11');
+var box12 = createBox('box12');
+var box13 = createBox('box13');
+shadowRoot.appendChild(box11);
+shadowRoot.appendChild(box12);
+shadowRoot.appendChild(box13);
+
+var nestedHost = document.createElement('div');
+var nestedShadowRoot = nestedHost.attachShadow({mode: 'closed'});
+var box21 = createBox('box21');
+var box22 = createBox('box22');
+var box23 = createBox('box23');
+nestedShadowRoot.appendChild(box21);
+nestedShadowRoot.appendChild(box22);
+nestedShadowRoot.appendChild(box23);
+
+shadowRoot.appendChild(nestedHost);
+
+var x12 = centerX(box12);
+var y12 = centerY(box12);
+var x22 = centerX(box22);
+var y22 = centerY(box22);
+
+var root3 = blockHost.attachShadow({mode: 'closed'});
+root3.appendChild(document.createTextNode('text1'));
+var root4 = inlineBlockHost.attachShadow({mode: 'closed'});
+root4.appendChild(document.createTextNode('text2'));
+
+test(function() {
+ assertElementsFromPoint('document', x22, y22, [host, document.body, document.documentElement]);
+ assertElementsFromPoint('document', centerX(blockHost), centerY(blockHost),
+ [blockHost, document.body, document.documentElement]);
+ assertElementsFromPoint('document', centerX(inlineBlockHost), centerY(inlineBlockHost),
+ [inlineBlockHost, document.body, document.documentElement]);
+ assertElementsFromPoint('document', centerX(submit), centerY(submit),
+ [submit, document.body, document.documentElement]);
+}, 'elementsFromPoint on the document root should not return elements in shadow trees');
+
+test(function() {
+ assert_not_equals(shadowRoot.elementsFromPoint(x12, y12).indexOf(box12), -1);
+ assert_not_equals(shadowRoot.elementsFromPoint(x22, y22).indexOf(nestedHost), -1);
+ assert_not_equals(nestedShadowRoot.elementsFromPoint(x22, y22).indexOf(box22), -1);
+}, 'elementsFromPoint on a shadow root should include elements in that shadow tree');
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-simple.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-simple.html
new file mode 100644
index 0000000000..4973121d7d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-simple.html
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<style>
+html, body {
+ margin: 0;
+ padding: 0;
+}
+body {
+ height: 500px;
+}
+#simpleDiv {
+ width: 200px;
+ height: 200px;
+ background-color: rgba(0,0,255,0.5);
+}
+#divWithPseudo {
+ position: absolute;
+ left: 50px;
+ top: 50px;
+ width: 100px;
+ height: 100px;
+ background-color: rgba(255,0,0,0.5);
+}
+#divWithPseudo::before {
+ position: absolute;
+ left: 20px;
+ top: 20px;
+ width: 100px;
+ height: 100px;
+ content: "::before";
+ background-color: rgba(255,0,0,0.5);
+ z-index: 9999;
+}
+#divBetweenPseudo {
+ position: absolute;
+ left: 100px;
+ top: 100px;
+ width: 100px;
+ height: 100px;
+ background-color: rgba(0,255,0,0.5);
+}
+#withMargin {
+ margin-top: -15px;
+ width: 200px;
+ height: 200px;
+ background-color: rgba(0,0,0,0.5);
+}
+#inlineSpan {
+ float: right;
+ background-color: yellow;
+ width: 100px;
+ height: 1em;
+}
+#noPointerEvents {
+ position: absolute;
+ left: 50px;
+ top: 50px;
+ width: 100px;
+ height: 300px;
+ background-color: rgba(0,0,0,0.1);
+ pointer-events: none;
+}
+#threeD {
+ position: absolute;
+ transform: translate3d(-100px, -100px, 10px);
+ left: 140px;
+ top: 140px;
+ width: 200px;
+ height: 50px;
+ background-color: rgba(255,255,255,0.5);
+}
+</style>
+<div id="simpleDiv"></div>
+<div id="divWithPseudo"></div>
+<div id="divBetweenPseudo"></div>
+<div id="withMargin"><span id="inlineSpan"></span></div>
+<div id="noPointerEvents"></div>
+<div id="threeD"></div>
+<script>
+var body = document.body;
+var html = document.documentElement;
+test(function() {
+ checkElementsFromPointFourCorners('document', 'simpleDiv',
+ [simpleDiv, body, html],
+ [simpleDiv, body, html],
+ [withMargin, simpleDiv, body, html],
+ [divBetweenPseudo, inlineSpan, withMargin, simpleDiv, body, html]);
+}, "elementsFromPoint for each corner of a simple div");
+
+test(function() {
+ checkElementsFromPointFourCorners('document', 'divWithPseudo',
+ [threeD, divWithPseudo, simpleDiv, body, html],
+ [threeD, divWithPseudo, simpleDiv, body, html],
+ [divWithPseudo, simpleDiv, body, html],
+ [divWithPseudo, divBetweenPseudo, divWithPseudo, simpleDiv, body, html]);
+}, "elementsFromPoint for each corner of a div that has a pseudo-element");
+
+test(function() {
+ checkElementsFromPointFourCorners('document', 'divBetweenPseudo',
+ [divWithPseudo, divBetweenPseudo, divWithPseudo, simpleDiv, body, html],
+ [divBetweenPseudo, simpleDiv, body, html],
+ [divBetweenPseudo, inlineSpan, withMargin, simpleDiv, body, html],
+ [divBetweenPseudo, inlineSpan, withMargin, simpleDiv, body, html]);
+}, "elementsFromPoint for each corner of a div that is between another div and its pseudo-element");
+
+test(function() {
+ checkElementsFromPointFourCorners('document', 'withMargin',
+ [withMargin, simpleDiv, body, html],
+ [divBetweenPseudo, inlineSpan, withMargin, simpleDiv, body, html],
+ [withMargin, body, html],
+ [withMargin, body, html]);
+}, "elementsFromPoint for each corner of a div that has a margin");
+
+test(function() {
+ checkElementsFromPointFourCorners('document', 'noPointerEvents',
+ [threeD, divWithPseudo, simpleDiv, body, html],
+ [threeD, divWithPseudo, simpleDiv, body, html],
+ [withMargin, body, html],
+ [withMargin, body, html]);
+}, "elementsFromPoint for each corner of a div with pointer-events:none");
+
+test(function() {
+ checkElementsFromPointFourCorners('document', 'threeD',
+ [threeD, simpleDiv, body, html],
+ [threeD, body, html],
+ [threeD, simpleDiv, body, html],
+ [threeD, body, html]);
+}, "elementsFromPoint for each corner of a div with a 3d transform");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-svg-text.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-svg-text.html
new file mode 100644
index 0000000000..9729724e67
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-svg-text.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<title>Document.prototype.elementsFromPoint on SVG text content elements</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-document-elementsfrompoint">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<style>
+html, body {
+ margin: 0;
+ padding: 0;
+}
+#svg {
+ margin: 100px;
+ background-color: rgba(0,180,0,0.2);
+}
+text {
+ font-size: 50px;
+}
+</style>
+<div id='sandbox'>
+ <svg id='svg' width='300' height='300'>
+ <defs>
+ <path id="path" d="M10,170h1000"/>
+ </defs>
+ <text id="text1" x="10" y="50">Some text</text>
+ <text id="text2" x="10" y="110"><tspan id="tspan1">Some text</tspan></text>
+ <text id="text3"><textPath id="textpath1" xlink:href="#path">Some text</textPath></text>
+ <text id="text4" x="10" y="230">Text under<tspan id="tspan2" x="10">Text over</tspan></text>
+ </svg>
+</div>
+<script>
+test(function() {
+ assertElementsFromPoint('document', 125, 125,
+ [text1, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside a <text>');
+
+test(function() {
+ assertElementsFromPoint('document', 125, 185,
+ [tspan1, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside a <tspan> nested in a <text> without content');
+
+test(function() {
+ assertElementsFromPoint('document', 125, 245,
+ [textpath1, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside a <textPath> nested in a <text> without content');
+
+test(function() {
+ assertElementsFromPoint('document', 125, 305,
+ [tspan2, text4, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside an overlapping <tspan> nested in a <text>');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-svg.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-svg.html
new file mode 100644
index 0000000000..8535228aba
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-svg.html
@@ -0,0 +1,67 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<style>
+html, body {
+ margin: 0;
+ padding: 0;
+}
+#svg {
+ margin: 100px;
+ background-color: rgba(0,180,0,0.2);
+}
+rect {
+ fill: rgba(180,0,0,0.2);
+}
+#topLeftRect2NoHitTest {
+ pointer-events: none;
+}
+</style>
+<div id='sandbox'>
+ <svg id='svg' width='300' height='300'>
+ <rect id='topLeftRect1' x='5' y='5' width='90' height='90'/>
+ <rect id='topLeftRect2NoHitTest' x='10' y='10' width='80' height='80'/>
+ <rect id='topLeftRect3' x='15' y='15' width='70' height='70'/>
+
+ <g id='middleG1'>
+ <g id='middleG2'>
+ <rect id='middleRect1' x='105' y='105' width='90' height='90'/>
+ <rect id='middleRect2' x='110' y='110' width='80' height='80'/>
+ </g>
+ </g>
+
+ <g id='bottomLeftG'>
+ <image id='bottomLeftImage1' x='5' y='205' width='90' height='90' xlink:href='data:image/svg+xml;utf8,<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="rgba(180,0,0,0.2)"/></svg>'/>
+ <image id='bottomLeftImage2' x='10' y='210' width='80' height='80' xlink:href='data:image/svg+xml;utf8,<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg"><rect width="100%" height="100%" fill="rgba(180,0,0,0.2)"/></svg>'/>
+ </g>
+
+ <g id='bottomRightG1' transform='translate(300, 300)'>
+ <g id='bottomRightG2' transform='translate(-100, -100)'>
+ <rect id='bottomRightRect1' x='5' y='5' width='90' height='90'/>
+ <rect id='bottomRightRect2' x='110' y='110' width='80' height='80' transform='translate(-100, -100)'/>
+ </g>
+ </g>
+ </svg>
+</div>
+<script>
+test(function() {
+ assertElementsFromPoint('document', 125, 125,
+ [topLeftRect3, topLeftRect1, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside two rects');
+
+test(function() {
+ assertElementsFromPoint('document', 225, 225,
+ [middleRect2, middleRect1, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside two rects that are inside a <g>');
+
+test(function() {
+ assertElementsFromPoint('document', 125, 325,
+ [bottomLeftImage2, bottomLeftImage1, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside two images');
+
+test(function() {
+ assertElementsFromPoint('document', 325, 325,
+ [bottomRightRect2, bottomRightRect1, svg, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for a point inside transformed rects and <g>');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint-table.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-table.html
new file mode 100644
index 0000000000..9ecb614b66
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint-table.html
@@ -0,0 +1,99 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/elementsFromPoint.js"></script>
+<style>
+html, body {
+ margin: 0;
+ padding: 0;
+}
+#testtable {
+ margin: 100px;
+ width: 200px;
+ height: 200px;
+ background-color: rgba(0,180,0,0.2);
+}
+#testtable tr {
+ background-color: rgba(180,0,0,0.2);
+}
+#testtable td {
+ background-color: rgba(0,0,180,0.2);
+}
+.rtl {
+ direction: rtl;
+}
+.tblr {
+ writing-mode: vertical-lr;
+}
+</style>
+<div id='sandbox'>
+ <table id='testtable'>
+ <tr id='tr1'>
+ <td id='td11'></td>
+ <td id='td12'></td>
+ <td id='td13'></td>
+ <td id='td14'></td>
+ </tr>
+ <tr id='tr2'>
+ <td id='td21'></td>
+ <td id='td22'></td>
+ <td id='td23'></td>
+ <td id='td24'></td>
+ </tr>
+ <tr id='tr3'>
+ <td id='td31'></td>
+ <td id='td32'></td>
+ <td id='td33'></td>
+ <td id='td34'></td>
+ </tr>
+ <tr id='tr4'>
+ <td id='td41'></td>
+ <td id='td42'></td>
+ <td id='td43'></td>
+ <td id='td44'></td>
+ </tr>
+ </table>
+</div>
+<script>
+test(function() {
+ assertElementsFromPoint('document', 125, 125,
+ [td11, testtable, sandbox, document.body, document.documentElement]);
+ assertElementsFromPoint('document', 275, 125,
+ [td14, testtable, sandbox, document.body, document.documentElement]);
+ assertElementsFromPoint('document', 175, 175,
+ [td22, testtable, sandbox, document.body, document.documentElement]);
+ assertElementsFromPoint('document', 125, 275,
+ [td41, testtable, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for points inside table cells');
+
+test(function() {
+ assertElementsFromPoint('document', 100, 100,
+ [testtable, sandbox, document.body, document.documentElement]);
+ assertElementsFromPoint('document', 199, 199,
+ [testtable, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for points between table cells');
+
+testtable.setAttribute('class', 'rtl');
+test(function() {
+ assertElementsFromPoint('document', 125, 125,
+ [td14, testtable, sandbox, document.body, document.documentElement]);
+ assertElementsFromPoint('document', 275, 125,
+ [td11, testtable, sandbox, document.body, document.documentElement]);
+ assertElementsFromPoint('document', 100, 100,
+ [testtable, sandbox, document.body, document.documentElement]);
+ assertElementsFromPoint('document', 199, 199,
+ [testtable, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for points inside cells in a right-to-left table');
+
+testtable.setAttribute('class', 'tblr');
+test(function() {
+ assertElementsFromPoint('document', 125, 275,
+ [td14, testtable, sandbox, document.body, document.documentElement]);
+ assertElementsFromPoint('document', 275, 125,
+ [td41, testtable, sandbox, document.body, document.documentElement]);
+ assertElementsFromPoint('document', 100, 100,
+ [testtable, sandbox, document.body, document.documentElement]);
+ assertElementsFromPoint('document', 199, 199,
+ [testtable, sandbox, document.body, document.documentElement]);
+}, 'elementsFromPoint for points inside cells in a flipped (writing-mode:vertical-lr) table');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/elementsFromPoint.html b/testing/web-platform/tests/css/cssom-view/elementsFromPoint.html
new file mode 100644
index 0000000000..bf883a1423
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/elementsFromPoint.html
@@ -0,0 +1,150 @@
+<!DOCTYPE html>
+<title>cssom-view - elementsFromPoint</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ .size {
+ width:60px;
+ height:60px;
+ }
+ .overlay {
+ position:absolute;
+ top:69px;
+ pointer-events:none;
+ }
+ .purple {
+ background-color: rebeccapurple;
+ }
+ .yellow {
+ background-color: yellow;
+ }
+ .teal {
+ background-color: teal;
+ }
+ .pink {
+ background-color: pink;
+ }
+</style>
+<body>
+ <div id='purple' class="size purple" >&nbsp;</div>
+ <div id='yellow' class="size yellow">&nbsp;</div>
+ <div id='teal' class="size overlay teal">&nbsp;</div>
+ <iframe id=iframe-1 src="iframe.html" style='display:none;position:absolute; left:300px;'></iframe>
+ <iframe id=iframe-2 src="iframe.html" width="" height=""></iframe>
+ <iframe id=iframe-3 width="" height=""></iframe>
+ <svg id=squiggle xmlns="http://www.w3.org/2000/svg" height="98" width="500" viewBox="0 0 581 98">
+ <path stroke-dashoffset="0.00" stroke-dasharray="" d="M62.9 14.9c-25-7.74-56.6 4.8-60.4 24.3-3.73 19.6 21.6 35 39.6 37.6 42.8 6.2 72.9-53.4 116-58.9 65-18.2 191 101 215 28.8 5-16.7-7-49.1-34-44-34 11.5-31 46.5-14 69.3 9.38 12.6 24.2 20.6 39.8 22.9 91.4 9.05 102-98.9 176-86.7 18.8 3.81 33 17.3 36.7 34.6 2.01 10.2.124 21.1-5.18 30.1" stroke="#000" stroke-width="4.3" fill="none">
+ </path>
+ </svg>
+ <svg id=svg-transform width="180" height="140"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:xlink="http://www.w3.org/1999/xlink">
+
+ <!-- Now we add a text element and apply rotate and translate to both -->
+ <rect x="50" y="50" height="60" width="60" style="stroke:#000; fill: #0086B2" transform="translate(30) rotate(45 50 50)"></rect>
+ <text x="60" y="105" transform="translate(30) rotate(45 50 50)"> Hello! </text>
+
+</svg>
+ <div id='pink' class='size pink' style='transform: translate(10px)'>&nbsp;</div>
+ <div id='anotherteal' class='size teal' style='pointer-events:none'>Another teal</div>
+ <script>
+ setup({explicit_done:true});
+ window.onload = function () {
+ test(function () {
+ assert_array_equals(document.elementsFromPoint(-1, -1), [],
+ "both co-ordinates passed in are negative so should have returned a []");
+ assert_array_equals(document.elementsFromPoint(-1, -1), [],
+ "x co-ordinates passed in are negative so should have returned a []");
+ assert_array_equals(document.elementsFromPoint(-1, -1), [],
+ "y co-ordinates passed in are negative so should have returned a []");
+ }, "Negative co-ordinates");
+
+ test(function () {
+ var viewportX = window.innerWidth;
+ var viewportY = window.innerHeight;
+ assert_array_equals(document.elementsFromPoint(viewportX + 100, 10), [],
+ "X co-ordinates larger than viewport so should have returned a []");
+ assert_array_equals(document.elementsFromPoint(10, viewportY + 100), [],
+ "Y co-ordinates larger than viewport so should have returned a []");
+ assert_array_equals(document.elementsFromPoint(viewportX + 100, viewportY + 100), [],
+ "X, Y co-ordinates larger than viewport so should have returned a []");
+ }, "co-ordinates larger than the viewport");
+
+ test(function () {
+ var viewportX = window.frames[1].innerWidth;
+ var viewportY = window.frames[1].innerHeight;
+ var iframeRect = document.getElementById('iframe-2').getBoundingClientRect();
+ assert_array_equals([], window.frames[1].document.elementsFromPoint(iframeRect.right + viewportX + 100, 10),
+ "X co-ordinates larger than viewport so should have returned a []");
+ assert_array_equals([], window.frames[1].document.elementsFromPoint(10, iframeRect.bottom + viewportY + 100),
+ "Y co-ordinates larger than viewport so should have returned a []");
+ assert_array_equals([], window.frames[1].document.elementsFromPoint(iframeRect.right + viewportX + 100,
+ iframeRect.bottom + viewportY + 100),
+ "X, Y co-ordinates larger than viewport so should have returned a []");
+ }, "co-ordinates larger than the viewport from in iframe");
+
+ test(function () {
+ assert_array_equals(document.elementsFromPoint(10, 10),
+ [document.getElementById('purple'), document.body, document.querySelector('html')],
+ "Should have returned a sequence with `[purple element, document.body, html]`");
+ }, "Return first element that is the target for hit testing");
+
+ test(function () {
+ assert_array_equals(document.elementsFromPoint(10, 80),
+ [document.getElementById('yellow'), document.body, document.querySelector('html')],
+ "Should have returned a sequence with `[yellow element, document.body, html]`");
+ }, "First element to get mouse events with pointer-events css");
+
+ test(function () {
+ var svg = document.getElementById('squiggle');
+ var svgRect = svg.getBoundingClientRect();
+ assert_array_equals(document.elementsFromPoint(svgRect.left + Math.round(svgRect.width/2),
+ svgRect.top + Math.round(svgRect.height/2)),
+ [svg, document.body, document.querySelector('html')],
+ "Should have returned a sequence with [svg, body, html]");
+ }, "SVG element at x,y");
+
+ test(function () {
+ var svg = document.getElementById('svg-transform');
+ var svgRect = svg.getBoundingClientRect();
+ assert_array_equals(document.elementsFromPoint(svgRect.left + Math.round(svgRect.width/2),
+ svgRect.top + Math.round(svgRect.height/2)),
+ [svg.querySelector("rect"), svg, document.body, document.querySelector('html')],
+ "Should have returned [svg rect, svg, body, html]");
+
+ var pink = document.getElementById('pink');
+ var pinkRect = pink.getBoundingClientRect();
+ assert_array_equals(document.elementsFromPoint(pinkRect.left + Math.round(pinkRect.width/2),
+ pinkRect.top + Math.round(pinkRect.height/2)),
+ [pink, document.body, document.querySelector('html')],
+ "Should have returned a sequence with [pink, body, html]");
+
+ }, "transformed element at x,y");
+
+ test(function () {
+ var anotherteal = document.getElementById('anotherteal');
+ var anothertealRect = anotherteal.getBoundingClientRect();
+ assert_array_equals(document.elementsFromPoint(anothertealRect.left + Math.round(anothertealRect.width/2),
+ anothertealRect.top + Math.round(anothertealRect.height/2)),
+ [document.body, document.querySelector('html')],
+ "Should have returned the sequence [body, html]");
+
+ var doc = frames[1].document;
+ assert_array_equals([doc.querySelector('html')], doc.elementsFromPoint(0, 100),
+ "Should have returned the sequence [html]")
+
+ var doc = frames[2].document;
+ doc.removeChild(doc.documentElement);
+ assert_array_equals(doc.elementsFromPoint(0, 0), [],
+ "Should have returned [] as no root element");
+
+ }, "no hit target at x,y");
+
+ test(function () {
+ var doc = document.implementation.createHTMLDocument('foo');
+ assert_array_equals(doc.elementsFromPoint(0, 0), [],
+ "Should have returned []")
+ }, "No viewport available");
+ done();
+ }
+ </script>
diff --git a/testing/web-platform/tests/css/cssom-view/getBoundingClientRect-empty-inline.html b/testing/web-platform/tests/css/cssom-view/getBoundingClientRect-empty-inline.html
new file mode 100644
index 0000000000..f5c6bee985
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getBoundingClientRect-empty-inline.html
@@ -0,0 +1,35 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect">
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+div {
+ font: 10px/1 Ahem;
+}
+.inline-block {
+ display: inline-block;
+ height: 100px;
+}
+</style>
+<body>
+ <div>
+ <span id="empty"></span>
+ <span class="inline-block"></span>
+ </div>
+<script>
+document.fonts.ready.then(() => {
+run(document.getElementById('empty'));
+function run(element) {
+ test(() => {
+ let rect = element.getBoundingClientRect();
+ let cb = element.parentElement;
+ let cb_rect = cb.getBoundingClientRect();
+ assert_equals(rect.y - cb_rect.y, 92, "y");
+ assert_equals(rect.height, 10, "height");
+ });
+}
+});
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/getBoundingClientRect-shy.html b/testing/web-platform/tests/css/cssom-view/getBoundingClientRect-shy.html
new file mode 100644
index 0000000000..b27a788460
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getBoundingClientRect-shy.html
@@ -0,0 +1,93 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect">
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#container {
+ font-family: Ahem;
+ font-size: 10px;
+ line-height: 1;
+ width: 10ch;
+}
+</style>
+<body>
+ <div id="container">
+ <div id="test1">123456&shy;789012</div>
+ <div id="test2">123456&shy;789012&shy;345678&shy;901234</div>
+ <div id="test3">12&shy;456&shy;789012</div>
+ <div>123<span id="test4">&shy;</span>456<span id="test5">&shy;</span>789012</div>
+ </div>
+<script>
+function getBoundingClientRect(node, start, end) {
+ const range = document.createRange();
+ range.setStart(node, start);
+ range.setEnd(node, end);
+ const rect = range.getBoundingClientRect();
+ return rect;
+}
+
+test(() => {
+ const rect = getBoundingClientRect(test1.firstChild, 0, 5);
+ assert_equals(rect.width, 50);
+}, "Range 0-5 should not include the hyphen.");
+
+test(() => {
+ const rect = getBoundingClientRect(test1.firstChild, 1, 5);
+ assert_equals(rect.width, 40);
+}, "Range 1-5 should not include the hyphen.");
+
+test(() => {
+ const rect = getBoundingClientRect(test1.firstChild, 0, 6);
+ assert_equals(rect.width, 60);
+}, "Range 0-6 should not include the hyphen.");
+
+test(() => {
+ const rect = getBoundingClientRect(test1.firstChild, 1, 6);
+ assert_equals(rect.width, 50);
+}, "Range 1-6 should not include the hyphen.");
+
+test(() => {
+ const rect = getBoundingClientRect(test1.firstChild, 0, 7);
+ assert_equals(rect.width, 70);
+}, "Range 0-7 should include the hyphen.");
+
+test(() => {
+ const rect = getBoundingClientRect(test1.firstChild, 1, 7);
+ assert_equals(rect.width, 60);
+}, "Range 1-7 should include the hyphen.");
+
+test(() => {
+ const rect = getBoundingClientRect(test1.firstChild, 0, 8);
+ assert_equals(rect.width, 70);
+ assert_equals(rect.height, 20);
+}, "Range 0-8 should include the hyphen.");
+
+test(() => {
+ const rect = getBoundingClientRect(test2.firstChild, 0, 8);
+ assert_equals(rect.width, 70);
+ assert_equals(rect.height, 20);
+}, "Range 0-8 should include only the first hyphen.");
+
+test(() => {
+ const rect = getBoundingClientRect(test3.firstChild, 2, 3);
+ assert_equals(rect.width, 0);
+}, "Collapsed soft-hyphen should be 0 width.");
+
+test(() => {
+ const rect = getBoundingClientRect(test3.firstChild, 6, 7);
+ assert_equals(rect.width, 10);
+}, "Rendered soft-hyphen should have a width.");
+
+test(() => {
+ const rect = getBoundingClientRect(test4.firstChild, 0, 1);
+ assert_equals(rect.width, 0);
+}, "Collapsed soft-hyphen in a span should be 0 width.");
+
+test(() => {
+ const rect = getBoundingClientRect(test5.firstChild, 0, 1);
+ assert_equals(rect.width, 10);
+}, "Rendered soft-hyphen in a span should have a width.");
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/getBoundingClientRect-svg.html b/testing/web-platform/tests/css/cssom-view/getBoundingClientRect-svg.html
new file mode 100644
index 0000000000..562c592224
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getBoundingClientRect-svg.html
@@ -0,0 +1,62 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-getboundingclientrect">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-range-getboundingclientrect">
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+text {
+ font-family: Ahem;
+ font-size: 10px;
+ line-height: 1;
+ width: 10ch;
+}
+</style>
+<body>
+
+<svg width="800" height="100">
+<text x="20" y="20" id="text-1">An SVG text element with no "transform" attribute</text>
+<text x="20" y="50" id="text-2" transform="matrix(2,0,0,2,0,0)">An SVG with a "transform" attribute</text>
+<text x="20" y="80" id="text-3" rotate="45">An SVG with a "rotate" attribute</text>
+</svg>
+
+<script>
+function getBoundingClientRect(node, start, end) {
+ const range = document.createRange();
+ range.setStart(node, start);
+ range.setEnd(node, end);
+ const rect = range.getBoundingClientRect();
+ return rect;
+}
+
+test(() => {
+ const element = document.querySelector('#text-1');
+ const elementRect = element.getBoundingClientRect();
+ const rangeRect = getBoundingClientRect(element.firstChild, 0, element.firstChild.length);
+ assert_approx_equals(elementRect.x, rangeRect.x, 1);
+ assert_approx_equals(elementRect.y, rangeRect.y, 1);
+ assert_approx_equals(elementRect.width, rangeRect.width, 1);
+ assert_approx_equals(elementRect.height, rangeRect.height, 1);
+}, 'Element.getBoundingClientRect() and Range.getBoudingClientRect() should match for an SVG <text>');
+
+test(() => {
+ const element = document.querySelector('#text-2');
+ const elementRect = element.getBoundingClientRect();
+ const rangeRect = getBoundingClientRect(element.firstChild, 0, element.firstChild.length);
+ assert_approx_equals(elementRect.x, rangeRect.x, 1);
+ assert_approx_equals(elementRect.y, rangeRect.y, 1);
+ assert_approx_equals(elementRect.width, rangeRect.width, 1);
+ assert_approx_equals(elementRect.height, rangeRect.height, 1);
+}, 'Element.getBoundingClientRect() and Range.getBoudingClientRect() should match for an SVG <text> with a transform');
+
+test(() => {
+ const element = document.querySelector('#text-3');
+ const elementRect = element.getBoundingClientRect();
+ const rangeRect = getBoundingClientRect(element.firstChild, 0, element.firstChild.length);
+ assert_approx_equals(elementRect.x, rangeRect.x, 1);
+ assert_approx_equals(elementRect.y, rangeRect.y, 1);
+ assert_approx_equals(elementRect.width, rangeRect.width, 1);
+ assert_approx_equals(elementRect.height, rangeRect.height, 1);
+}, 'Element.getBoundingClientRect() and Range.getBoudingClientRect() should match for an SVG <text> with a rotate');
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/getClientRects-br-htb-ltr.html b/testing/web-platform/tests/css/cssom-view/getClientRects-br-htb-ltr.html
new file mode 100644
index 0000000000..e952d61b67
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getClientRects-br-htb-ltr.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-element-getclientrects">
+<div style="position:absolute; top:0; left:0; width:200px; height:100px; writing-mode:horizontal-tb; direction:ltr;">
+ <br id="child">
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=>{
+ let r = document.getElementById("child").getClientRects()[0];
+ assert_equals(r.left, 0);
+ assert_greater_than_equal(r.top, 0);
+ assert_less_than(r.top, 50);
+ }, "Position of the BR element");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/getClientRects-br-htb-rtl.html b/testing/web-platform/tests/css/cssom-view/getClientRects-br-htb-rtl.html
new file mode 100644
index 0000000000..24dbc929a6
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getClientRects-br-htb-rtl.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-element-getclientrects">
+<div style="position:absolute; top:0; left:0; width:200px; height:100px; writing-mode:horizontal-tb; direction:rtl;">
+ <br id="child">
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=>{
+ let r = document.getElementById("child").getClientRects()[0];
+ assert_equals(r.left, 200);
+ assert_greater_than_equal(r.top, 0);
+ assert_less_than(r.top, 50);
+ }, "Position of the BR element");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/getClientRects-br-vlr-ltr.html b/testing/web-platform/tests/css/cssom-view/getClientRects-br-vlr-ltr.html
new file mode 100644
index 0000000000..b324342d64
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getClientRects-br-vlr-ltr.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-element-getclientrects">
+<div style="position:absolute; top:0; left:0; width:200px; height:100px; writing-mode:vertical-lr; direction:ltr;">
+ <br id="child">
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=>{
+ let r = document.getElementById("child").getClientRects()[0];
+ assert_greater_than_equal(r.left, 0);
+ assert_less_than(r.left, 50);
+ assert_equals(r.top, 0);
+ }, "Position of the BR element");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/getClientRects-br-vlr-rtl.html b/testing/web-platform/tests/css/cssom-view/getClientRects-br-vlr-rtl.html
new file mode 100644
index 0000000000..3a36f97306
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getClientRects-br-vlr-rtl.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-element-getclientrects">
+<div style="position:absolute; top:0; left:0; width:200px; height:100px; writing-mode:vertical-lr; direction:rtl;">
+ <br id="child">
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=>{
+ let r = document.getElementById("child").getClientRects()[0];
+ assert_greater_than_equal(r.left, 0);
+ assert_less_than(r.left, 50);
+ assert_equals(r.top, 100);
+ }, "Position of the BR element");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/getClientRects-br-vrl-ltr.html b/testing/web-platform/tests/css/cssom-view/getClientRects-br-vrl-ltr.html
new file mode 100644
index 0000000000..c0be872a0f
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getClientRects-br-vrl-ltr.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-element-getclientrects">
+<div style="position:absolute; top:0; left:0; width:200px; height:100px; writing-mode:vertical-rl; direction:ltr;">
+ <br id="child">
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=>{
+ let r = document.getElementById("child").getClientRects()[0];
+ assert_greater_than(r.left, 150);
+ assert_less_than(r.left, 200);
+ assert_equals(r.top, 0);
+ }, "Position of the BR element");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/getClientRects-br-vrl-rtl.html b/testing/web-platform/tests/css/cssom-view/getClientRects-br-vrl-rtl.html
new file mode 100644
index 0000000000..c5a3062cf4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getClientRects-br-vrl-rtl.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-element-getclientrects">
+<div style="position:absolute; top:0; left:0; width:200px; height:100px; writing-mode:vertical-rl; direction:rtl;">
+ <br id="child">
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(()=>{
+ let r = document.getElementById("child").getClientRects()[0];
+ assert_greater_than(r.left, 150);
+ assert_less_than(r.left, 200);
+ assert_equals(r.top, 100);
+ }, "Position of the BR element");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/getClientRects-inline-atomic-child.html b/testing/web-platform/tests/css/cssom-view/getClientRects-inline-atomic-child.html
new file mode 100644
index 0000000000..f1f6fc5abc
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getClientRects-inline-atomic-child.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-element-getclientrects">
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+inline-block {
+ display: inline-block;
+ height: 100px;
+}
+</style>
+<body>
+ <div>
+ <span class="target">
+ <inline-block></inline-block>
+ </span>
+ </div>
+ <div>
+ <span class="target">
+ <span>
+ <inline-block></inline-block>
+ </span>
+ </span>
+ </div>
+ <div>
+ <span class="target">
+ <span>
+ <span>
+ <inline-block></inline-block>
+ </span>
+ </span>
+ </span>
+ </div>
+<script>
+for (let element of document.getElementsByClassName('target')) {
+ let rects = element.getClientRects();
+ test(() => {
+ assert_equals(rects.length, 1);
+ for (let rect of rects)
+ assert_not_equals(rect.height, 100);
+ });
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/getClientRects-inline-inline-child.html b/testing/web-platform/tests/css/cssom-view/getClientRects-inline-inline-child.html
new file mode 100644
index 0000000000..9a03ccdbcc
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getClientRects-inline-inline-child.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-element-getclientrects">
+<link rel="author" title="Koji Ishii" href="mailto:kojii@chromium.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+ <div>
+ <span>test</span>
+ <span id="vertical-align-sub-as-grand-child"
+ title="sub element in a child inline box should not be included">
+ <span>
+ <sub class="not-include" style="vertical-align: sub">subscript</sub>
+ </span>
+ </span>
+ <span>test</span>
+ </div>
+<script>
+testTargetDoesNotInclude('vertical-align-sub-as-grand-child');
+
+function testTargetDoesNotInclude(target) {
+ target = document.getElementById(target);
+ test(() => {
+ let target_rects = target.getClientRects();
+
+ let not_include_rects = [];
+ for (let element of target.querySelectorAll('.not-include')) {
+ for (let rect of element.getClientRects())
+ not_include_rects.push(rect);
+ }
+ for (let rect of target_rects) {
+ for (let not_include_rect of not_include_rects) {
+ assert_rect_not_equals(rect, not_include_rect);
+ }
+ }
+ }, target.title);
+}
+
+function assert_rect_not_equals(rect1, rect2) {
+ assert_false(rectEquals(rect1, rect2),
+ `${rectToString(rect1)} and ${rectToString(rect2)} are not equal`);
+}
+
+function rectEquals(rect1, rect2) {
+ return rect1.x === rect2.x && rect1.y === rect2.y &&
+ rect1.width === rect2.width && rect1.height === rect2.height;
+}
+
+function rectToString(rect) {
+ return `{${rect.x}, ${rect.y} ${rect.width}x${rect.height}}`;
+}
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/getClientRects-inline.html b/testing/web-platform/tests/css/cssom-view/getClientRects-inline.html
new file mode 100644
index 0000000000..415e34a6f0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/getClientRects-inline.html
@@ -0,0 +1,55 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-element-getclientrects">
+<link rel="match" href="../reference/nothing.html">
+<style>
+ .container {
+ float: left;
+ width: 8em;
+ height: 7em;
+ padding: 1em;
+ color: red;
+ }
+ .correctionFluid {
+ position: absolute;
+ background: white;
+
+ /* Add some fluff to cover text ink-overflow. */
+ outline:2px solid white;
+ }
+</style>
+<p>There should be nothing below.</p>
+ <div class="container" style="writing-mode:horizontal-tb;">
+ <br><span class="child">FAIL</span>
+ </div>
+ <div class="container" style="writing-mode:vertical-lr;">
+ <br><span class="child">FAIL</span>
+ </div>
+ <div class="container" style="writing-mode:vertical-rl;">
+ <br><span class="child">FAIL</span>
+ </div>
+ <div class="container" style="writing-mode:horizontal-tb; direction:rtl;">
+ <br><span class="child">FAIL</span>
+ </div>
+ <div class="container" style="writing-mode:vertical-lr; direction:rtl;">
+ <br><span class="child">FAIL</span>
+ </div>
+ <div class="container" style="writing-mode:vertical-rl; direction:rtl;">
+ <br><span class="child">FAIL</span>
+ </div>
+<script>
+ // Create a white absolutely positioned box for each span.child
+ // element and cover it.
+
+ let elements = document.querySelectorAll("span.child");
+ elements.forEach((element)=> {
+ let correctionFluid = document.createElement("div");
+ correctionFluid.className = "correctionFluid";
+ var r = element.getClientRects()[0];
+ correctionFluid.style.left = r.left + "px";
+ correctionFluid.style.top = r.top + "px";
+ correctionFluid.style.width = r.width + "px";
+ correctionFluid.style.height = r.height + "px";
+ document.body.appendChild(correctionFluid);
+ });
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/historical.html b/testing/web-platform/tests/css/cssom-view/historical.html
new file mode 100644
index 0000000000..bf82eac115
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/historical.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>Historical features</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+test(function() {
+ assert_false("ClientRectList" in self);
+}, "Support for ClientRectList");
+
+test(function() {
+ assert_false("ClientRect" in self);
+}, "Support for ClientRect");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/htmlelement-offset-width-001.html b/testing/web-platform/tests/css/cssom-view/htmlelement-offset-width-001.html
new file mode 100644
index 0000000000..9171997e2a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/htmlelement-offset-width-001.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM View - 7 - element.offsetWidth detatches correctly</title>
+ <link rel="author" title="Michael Howell" href="mailto:michael@notriddle.com">
+ <link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-htmlelement-offsetwidth">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="element.offsetWidth returns 0 when there is no documentElement.">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="myDiv">...</div>
+ <div id="log"></div>
+ <script>
+ test(function() {
+ // These asserts need to be in this order to trigger a bug in Servo.
+ var el = document.createElement("div");
+ el.appendChild(document.createTextNode("..."));
+ assert_equals(el.offsetWidth, 0, "new element has offset width not equal to zero");
+ assert_not_equals(document.getElementById("myDiv"), 0, "element with content has offset width equal to zero");
+ var html = document.removeChild(document.documentElement);
+ // Put back the root element, so that testharnessreport.js can append the result.
+ this.add_cleanup(function() { document.appendChild(html); });
+ assert_equals(el.offsetWidth, 0, "new element has offset width not equal to zero after removing documentElement");
+ });
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/idlharness.html b/testing/web-platform/tests/css/cssom-view/idlharness.html
new file mode 100644
index 0000000000..c980a120e0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/idlharness.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<meta charset=utf-8>
+<!-- WARNING: These tests are preliminary and probably partly incorrect. -->
+<title>CSSOM View automated IDL tests</title>
+<meta name="timeout" content="long">
+<link rel="author" title="Ms2ger" href="mailto:Ms2ger@gmail.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#idl-index">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script src=/resources/WebIDLParser.js></script>
+<script src=/resources/idlharness.js></script>
+
+<input id="caretPositionInput" style="position: absolute; top: 0; left: 0; border: 0;">
+
+<h1>CSSOM View IDL tests</h1>
+<div id=log></div>
+
+<script>
+'use strict';
+
+const waitForLoad = new Promise(resolve => { addEventListener('load', resolve); });
+
+idl_test(
+ ['cssom-view'],
+ ['css-pseudo', 'cssom', 'uievents', 'SVG', 'html', 'dom'],
+ async idlArray => {
+ idlArray.add_objects({
+ Window: ['window'],
+ MediaQueryList: ['matchMedia("all")'],
+ MediaQueryListEvent: ['new MediaQueryListEvent("change")'],
+ Screen: ['screen'],
+ Document: ['document'],
+ CaretPosition: ['document.caretPositionFromPoint(5, 5)'],
+ Element: ['document.createElementNS("x", "y")'],
+ HTMLElement: ['document.createElement("div")'],
+ HTMLImageElement: ['document.createElement("img")'],
+ Range: ['new Range()'],
+ MouseEvent: ['new MouseEvent("foo")'],
+ Text: ['document.createTextNode("x")'],
+ VisualViewport: ['self.visualViewport'],
+ // CSSPseudoElement: [],
+ });
+
+ await waitForLoad;
+ }
+);
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/iframe.html b/testing/web-platform/tests/css/cssom-view/iframe.html
new file mode 100644
index 0000000000..e4413a7388
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/iframe.html
@@ -0,0 +1,4 @@
+<!DOCTYPE html>
+<style>
+ html, body { padding:9px; border:1px solid }
+</style>
diff --git a/testing/web-platform/tests/css/cssom-view/inheritance.html b/testing/web-platform/tests/css/cssom-view/inheritance.html
new file mode 100644
index 0000000000..8c381e7119
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/inheritance.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>Inheritance of CSSOM View properties</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#property-index">
+<meta name="assert" content="Properties inherit or not according to the spec.">
+<meta name="assert" content="Properties have initial values according to the spec.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/inheritance-testcommon.js"></script>
+</head>
+<body>
+<div id="container">
+ <div id="target"></div>
+</div>
+<script>
+assert_not_inherited('scroll-behavior', 'auto', 'smooth');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/long_scroll_composited-ref.html b/testing/web-platform/tests/css/cssom-view/long_scroll_composited-ref.html
new file mode 100644
index 0000000000..3278d33460
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/long_scroll_composited-ref.html
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Long scrolling should work properly</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+
+
+
+<style>
+.post {
+ height: 1000px;
+ width: 300px;
+ border: 1px solid black;
+
+}
+.before {
+ height: 213px;
+ border-top: 0;
+}
+.scroller {
+ overflow-y: scroll;
+ width: 500px;
+ height: 500px;
+ will-change: transform;
+}
+::-webkit-scrollbar {
+ display: none;
+}
+</style>
+
+<p>The number 7 should be visible in the scrolled window below.</p>
+
+<div id="scroller" class="scroller">
+ <div style="position: relative;">
+ <div style="position: relative;">
+ <div class="post before"></div>
+ <div class="post">7</div>
+ </div>
+ </div>
+</div>
+
+
diff --git a/testing/web-platform/tests/css/cssom-view/long_scroll_composited.html b/testing/web-platform/tests/css/cssom-view/long_scroll_composited.html
new file mode 100644
index 0000000000..68a468bfd9
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/long_scroll_composited.html
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Long scrolling should work properly</title>
+<link rel="author" href="mailto:masonf@chromium.org">
+<link rel="help" href="https://www.w3.org/TR/cssom-view/#scrolling">
+<link rel="match" href="long_scroll_composited-ref.html">
+
+<style>
+.post {
+ height: 1000px;
+ width: 300px;
+ border: 1px solid black;
+
+}
+.scroller {
+ overflow-y: scroll;
+ width: 500px;
+ height: 500px;
+ will-change: transform;
+}
+::-webkit-scrollbar {
+ display: none;
+}
+</style>
+
+<p>The number 7 should be visible in the scrolled window below.</p>
+
+<div id="scroller" class="scroller">
+ <div style="position: relative;">
+ <div style="position: relative;">
+ <div class="post">0</div>
+ <div class="post">1</div>
+ <div class="post">2</div>
+ <div class="post">3</div>
+ <div class="post">4</div>
+ <div class="post">5</div>
+ <div class="post">6</div>
+ <div class="post">7</div>
+ <div class="post">8</div>
+ <div class="post">9</div>
+ </div>
+ </div>
+</div>
+
+<script>
+onload = function() {
+ scroller=document.getElementById("scroller");
+ scroller.scrollTop = 6800;
+};
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/matchMedia-display-none-iframe.html b/testing/web-platform/tests/css/cssom-view/matchMedia-display-none-iframe.html
new file mode 100644
index 0000000000..08fcb3c538
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/matchMedia-display-none-iframe.html
@@ -0,0 +1,19 @@
+<!doctype html>
+<title>CSS Test: matchMedia works on display: none iframes</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-window-matchmedia">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ function frameLoaded(frame) {
+ test(function() {
+ assert_true(frame.contentWindow.matchMedia("all").matches);
+ }, "matchMedia should work in display: none iframes");
+ test(function() {
+ assert_true(frame.contentWindow.matchMedia("(min-width: 0)").matches);
+ assert_true(!frame.contentWindow.matchMedia("(min-width: 1px)").matches);
+ }, "matchMedia should assume a 0x0 viewport in display: none iframes");
+ }
+</script>
+<iframe style="display: none" onload="frameLoaded(this)"></iframe>
diff --git a/testing/web-platform/tests/css/cssom-view/matchMedia.html b/testing/web-platform/tests/css/cssom-view/matchMedia.html
new file mode 100644
index 0000000000..0a94c9850c
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/matchMedia.html
@@ -0,0 +1,79 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<meta name="flags" content="dom">
+<title>CSS Test: CSSOM View matchMedia and MediaQueryList</title>
+<link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#dom-window-matchmedia">
+<link rel="help" href="https://www.w3.org/TR/cssom-view-1/#the-mediaquerylist-interface">
+<link rel="help" href="https://www.w3.org/TR/cssom-1/#serializing-media-queries">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/matchMedia.js"></script>
+<div id="log"></div>
+<script>
+"use strict";
+
+test(() => {
+ assert_equals(
+ typeof window.matchMedia,
+ "function",
+ "FATAL ERROR: The window.matchMedia function is not present. The rest of the testsuite will fail to run."
+ );
+}, "window.matchMedia is a function");
+
+test(() => {
+ const mql = window.matchMedia("all");
+ assert_equals(mql.media, "all");
+ assert_true(mql.matches);
+}, 'window.matchMedia("all") matches');
+
+test(() => {
+ const mql = window.matchMedia("");
+ assert_equals(mql.media, "");
+ assert_true(mql.matches);
+}, 'window.matchMedia("") matches');
+
+test(() => {
+ const mql = window.matchMedia("(min-width: 1px)");
+ assert_equals(mql.media, "(min-width: 1px)");
+ assert_true(mql.matches);
+}, 'window.matchMedia("(min-width: 1px)") matches');
+
+test(() => {
+ const mql = window.matchMedia("::");
+ assert_true(mql instanceof MediaQueryList);
+ assert_equals(mql.media, "not all");
+ assert_false(mql.matches);
+}, 'media query with syntax error is serialized as "not all"');
+
+promise_test(async t => {
+ const iframe = await createIFrame(t, 200);
+ const mql = iframe.contentWindow.matchMedia("(max-width: 199px), all and (min-width: 200px)");
+ assert_equals(mql.media, "(max-width: 199px), (min-width: 200px)");
+ assert_true(mql.matches);
+}, 'iframe.matchMedia("(max-width: 199px), all and (min-width: 200px)") is serialized w/o "all"');
+
+promise_test(async t => {
+ const iframe = await createIFrame(t);
+ const mql = iframe.contentWindow.matchMedia("(min-aspect-ratio: 1/1)");
+ assert_true(mql.matches);
+}, 'iframe.matchMedia("(min-aspect-ratio: 1/1)") matches');
+
+promise_test(async t => {
+ const iframe = await createIFrame(t, 200);
+ const mql = iframe.contentWindow.matchMedia("(width: 200px)");
+ assert_true(mql.matches);
+}, 'iframe.matchMedia("(width: 200px)") matches');
+
+promise_test(async t => {
+ const iframe = await createIFrame(t, 200, 100);
+ const mql = iframe.contentWindow.matchMedia("(max-height: 50px)");
+ assert_false(mql.matches);
+}, 'iframe.matchMedia("(max-height: 50px)") matches');
+
+promise_test(async t => {
+ const iframe = await createIFrame(t, 200, 100);
+ const mql = iframe.contentWindow.matchMedia("(min-width: 150px)");
+ assert_true(mql.matches);
+}, 'iframe.matchMedia("(min-width: 150px)") matches');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/mouseEvent-offsetXY-svg.html b/testing/web-platform/tests/css/cssom-view/mouseEvent-offsetXY-svg.html
new file mode 100644
index 0000000000..c0bd164485
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/mouseEvent-offsetXY-svg.html
@@ -0,0 +1,27 @@
+<!doctype html>
+<title>MouseEvent.offsetX/Y returns coordinates relative to the root svg</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1684973">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/1508">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-mouseevent-offsetx">
+<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>
+<style>
+ body { margin: 0 }
+</style>
+<svg width=100 height=100>
+ <rect width=30 height=30 x=50 y=50 fill=green></rect>
+</svg>
+<script>
+let t = async_test();
+let rect = document.querySelector("rect");
+rect.addEventListener("click", t.step_func_done(function(e) {
+ assert_true(e.offsetX >= 50);
+ assert_true(e.offsetY >= 50);
+}))
+
+test_driver.click(rect);
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/mouseEvent.html b/testing/web-platform/tests/css/cssom-view/mouseEvent.html
new file mode 100644
index 0000000000..d509597292
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/mouseEvent.html
@@ -0,0 +1,42 @@
+<!doctype html>
+<meta charset=utf-8>
+<head>
+<title>CSSOM MouseEvent tests</title>
+<div style="background:lightblue; height:10000px">
+ Hello
+</div>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+test(function () {
+ var mouseEvent = new MouseEvent('mousedown', {clientX: 10, clientY: 20});
+ assert_equals(mouseEvent.x, 10);
+ assert_equals(mouseEvent.y, 20);
+ mouseEvent = new MouseEvent('mousedown', {clientX: 30, clientY: 40});
+ assert_equals(mouseEvent.x, 30);
+ assert_equals(mouseEvent.y, 40);
+}, 'MouseEvent\'s x and y must be equal to clientX and clientY.');
+
+test(function () {
+ var mouseEvent1 = new MouseEvent('mousedown', {clientX: 10, clientY: 20});
+ assert_equals(mouseEvent1.pageX, 10);
+ assert_equals(mouseEvent1.pageY, 20);
+ scrollBy(0, 5000);
+ assert_equals(mouseEvent1.pageX, 10);
+ assert_equals(mouseEvent1.pageY, 5020);
+
+ var mouseEvent2 = new MouseEvent('mousedown', {clientX: 10, clientY: 20});
+ assert_equals(mouseEvent2.pageX, 10);
+ assert_equals(mouseEvent2.pageY, 5020);
+}, 'MouseEvent\'s pageX and pageY attributes should be the sum of the scroll offset and clientX/clientY');
+
+test(function () {
+ var mouseEvent = new MouseEvent('mousedown', {clientX: 10, clientY: 20});
+ assert_equals(mouseEvent.offsetX, mouseEvent.pageX);
+ assert_equals(mouseEvent.offsetY, mouseEvent.pageY);
+ scrollBy(0, 5000);
+ assert_equals(mouseEvent.offsetX, mouseEvent.pageX);
+ assert_equals(mouseEvent.offsetY, mouseEvent.pageY);
+}, 'MouseEvent\'s offsetX/offsetY attributes should be the same value as its pageX/pageY attributes.');
+</script>
+</head>
diff --git a/testing/web-platform/tests/css/cssom-view/negativeMargins.html b/testing/web-platform/tests/css/cssom-view/negativeMargins.html
new file mode 100644
index 0000000000..0616e8b499
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/negativeMargins.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>cssom-view - elementFromPoint and elementsFromPoint dealing with negative margins</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<body>
+<div id="outer" style="background:yellow">
+ <div id="inner" style="width:100px; height:100px; margin-bottom:-100px; background:lime;"></div>
+ Hello
+</div>
+<script>
+ setup({explicit_done:true});
+ window.onload = function () {
+ var outer = document.getElementById('outer');
+ var inner = document.getElementById('inner');
+ var outerRect = outer.getBoundingClientRect();
+ test(function () {
+ assert_equals(document.elementFromPoint(outerRect.left + 1,
+ outerRect.top + 1),
+ outer,
+ "elementFromPoint should get outer element");
+ });
+ test(function () {
+ assert_array_equals(document.elementsFromPoint(outerRect.left + 1,
+ outerRect.top + 1),
+ [inner, outer, document.body, document.querySelector('html')],
+ "elementsFromPoint should get sequence [inner, outer, body, html]");
+ });
+ done();
+ };
+</script>
+</body>
diff --git a/testing/web-platform/tests/css/cssom-view/offsetParent-block-in-inline.html b/testing/web-platform/tests/css/cssom-view/offsetParent-block-in-inline.html
new file mode 100644
index 0000000000..f2bbb55f36
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/offsetParent-block-in-inline.html
@@ -0,0 +1,14 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#extensions-to-the-htmlelement-interface">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<span id="parent" style="position:relative;">
+ <div id="target"></div>
+</span>
+<script>
+test(function() {
+ const target = document.getElementById('target');
+ const parent = document.getElementById('parent');
+ assert_equals(target.offsetParent, parent);
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/offsetParent_element_test.html b/testing/web-platform/tests/css/cssom-view/offsetParent_element_test.html
new file mode 100644
index 0000000000..b538c23f9a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/offsetParent_element_test.html
@@ -0,0 +1,181 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+<title>CSSOM View —— offsetParent element test</title>
+<link rel="author" title="neo_and_rayi" href="mailto:1988wangxiao@gmail.com">
+<link rel="help" href="http://www.w3.org/TR/cssom-view/#extensions-to-the-htmlelement-interface">
+<link rel="help" href="http://www.w3.org/TR/cssom-view/#dom-htmlelement-offsetparent">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#fixed {
+ position: fixed;
+}
+
+#none-element {
+ display:none;
+}
+
+#relative-element {
+ position: relative;
+}
+
+#absolute-element {
+ position: absolute;
+}
+
+#transform-element {
+ transform: translateX(10px);
+}
+
+#perspective-element {
+ perspective: 10px;
+}
+
+#transform-style-preserve-3d-element {
+ transform-style: preserve-3d;
+}
+
+#filter-element {
+ filter: opacity(25%);
+}
+
+#contain-paint-element {
+ contain: paint;
+}
+</style>
+
+</head>
+<body>
+
+<div id="body-element-child"></div>
+
+<div id="relative-element">
+ <div id="relative-element-child"></div>
+</div>
+
+<div id="absolute-element">
+ <div id="absolute-element-child"></div>
+</div>
+
+<div id="transform-element">
+ <div id="transform-element-child"></div>
+</div>
+
+<div id="transform-style-preserve-3d-element">
+ <div id="transform-style-preserve-3d-element-child"></div>
+</div>
+
+<div id="perspective-element">
+ <div id="perspective-element-child"></div>
+</div>
+
+<div id="contain-paint-element">
+ <div id="contain-paint-element-child"></div>
+</div>
+
+<table id="table-element">
+ <caption>
+ <div id="caption-element-child"></div>
+ </caption>
+
+ <tbody>
+ <tr id="table-element-tr">
+ <td id="table-element-td">
+ <span id="table-element-child"></span>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<div id="none-element">
+ <a href="#" id="none-element-child-a"></a>
+ <p id="none-element-child-p"></p>
+ <video id="none-element-child-video"></video>
+ <audio id="none-element-child-audio"></audio>
+ <canvas id="none-element-child-canvas"></canvas>
+ <svg id="none-element-child-svg"></svg>
+</div>
+
+<div id="fixed">
+</div>
+
+<div id="log"></div>
+<script type="text/javascript">
+var getStyle = window.getComputedStyle;
+var html = document.documentElement;
+var body = document.body;
+var fixed_element = document.getElementById('fixed');
+var none_element = document.getElementById('none-element');
+
+var none_element_child_a = document.getElementById('none-element-child-a');
+var none_element_child_p = document.getElementById('none-element-child-p');
+var none_element_child_video = document.getElementById('none-element-child-video');
+var none_element_child_audio = document.getElementById('none-element-child-audio');
+var none_element_child_canvas = document.getElementById('none-element-child-canvas');
+var none_element_child_svg = document.getElementById('none-element-child-svg');
+
+var relative_element = document.getElementById('relative-element');
+var absolute_element = document.getElementById('absolute-element');
+var td_element = document.getElementsByTagName('td')[0];
+
+var body_element_child = document.getElementById('body-element-child');
+var relative_element_child = document.getElementById('relative-element-child');
+var absolute_element_child = document.getElementById('absolute-element-child');
+var table_element_child = document.getElementById('table-element-child');
+
+var caption_element_child = document.getElementById('caption-element-child');
+var table_element_tr = document.getElementById('table-element-tr');
+var table_element = document.getElementById('table-element');
+
+// The offsetParent attribute algorithm rule checking passed!
+test(function() {
+ assert_equals(html.offsetParent,null);
+ assert_equals(body.offsetParent,null);
+ assert_equals(fixed_element.offsetParent,null);
+ assert_equals(none_element.offsetParent,null);
+ assert_equals(none_element_child_a.offsetParent,null);
+ assert_equals(none_element_child_p.offsetParent,null);
+ assert_equals(none_element_child_video.offsetParent,null);
+ assert_equals(none_element_child_audio.offsetParent,null);
+ assert_equals(none_element_child_canvas.offsetParent,null);
+ assert_equals(none_element_child_svg.offsetParent,undefined);
+}, "Valid the algorithm rule of offsetParent check step 1");
+
+// The offsetParent attribute algorithm rule checking passed!
+test(function() {
+ assert_equals(body_element_child.offsetParent,body);
+ assert_equals(window.getComputedStyle(relative_element).position,'relative');
+ assert_equals(relative_element_child.offsetParent,relative_element);
+ assert_equals(window.getComputedStyle(absolute_element).position,'absolute');
+ assert_equals(absolute_element_child.offsetParent,absolute_element);
+ assert_equals(window.getComputedStyle(td_element).position,'static');
+ assert_equals(table_element_child.offsetParent,td_element);
+ assert_equals(window.getComputedStyle(table_element_tr).position,'static');
+ assert_equals(table_element_tr.offsetParent,table_element);
+ assert_equals(window.getComputedStyle(caption_element_child).position,'static');
+ assert_equals(caption_element_child.offsetParent,table_element);
+ assert_equals(window.getComputedStyle(td_element).position,'static');
+ assert_equals(td_element.offsetParent,table_element);
+
+ let transform_element = document.getElementById('transform-element');
+ assert_equals(transform_element.children[0].offsetParent, transform_element);
+
+ let perspective_element = document.getElementById('perspective-element');
+ assert_equals(perspective_element.children[0].offsetParent, perspective_element);
+
+ let transform_style_preserve_3d_element = document.getElementById('transform-style-preserve-3d-element');
+ assert_equals(transform_style_preserve_3d_element.children[0].offsetParent,
+ transform_style_preserve_3d_element);
+
+ let contain_paint_element = document.getElementById('contain-paint-element');
+ assert_equals(contain_paint_element.children[0].offsetParent, contain_paint_element);
+
+
+}, "Valid the algorithm rule of offsetParent check step 2");
+
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/offsetTop-offsetLeft-nested-offsetParents.html b/testing/web-platform/tests/css/cssom-view/offsetTop-offsetLeft-nested-offsetParents.html
new file mode 100644
index 0000000000..e9446708d3
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/offsetTop-offsetLeft-nested-offsetParents.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<link rel=author href="mailto:jarhar@chromium.org">
+<link rel=help href="https://drafts.csswg.org/cssom-view-1/#dom-htmlelement-offsettop">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+.square {
+ width: 10px;
+ height: 10px;
+ background-color: red;
+}
+#one {
+ position: absolute;
+ top: 10px;
+ left: 10px;
+}
+#two {
+ position: absolute;
+ top: 50px;
+ left: 50px;
+}
+span.square {
+ display: inline-block;
+}
+</style>
+
+<div id=one class=square>
+ <div id=two class=square>
+ <div class=square></div>
+ <span class=square></span><span id=target></span>
+ </div>
+</div>
+
+<script>
+test(() => {
+ assert_equals(target.offsetTop, 10, 'target.offsetTop');
+ assert_equals(target.offsetLeft, 10, 'target.offsetLeft');
+}, 'Verifies that offsetTop and offsetLeft only go up one offsetParent when there are multiple nested offsetParents.');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/offsetTopLeft-border-box.html b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-border-box.html
new file mode 100644
index 0000000000..8cabf6478f
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-border-box.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<link rel="stylesheet" type="text/css" href="/fonts/ahem.css" />
+<style>
+
+.container {
+ position: relative;
+ font: 20px/1 Ahem;
+ width: 150px;
+ height: 100px;
+ padding: 2px 10px;
+ border-width: 3px 6px;
+ border-style: solid;
+ box-sizing: border-box;
+}
+
+.target { background: grey; }
+.hl { writing-mode:horizontal-tb; }
+.vlr { writing-mode:vertical-lr; }
+</style>
+<div id=tests>
+<div class="container hl">
+ <span class="target">x</span>
+</div>
+<div class="container vlr">
+ <span class="target">x</span>
+</div>
+<div class="container hl">
+ <div class="target">x</div>
+</div>
+<div class="container vlr">
+ <div class="target">x</div>
+</div>
+</div>
+<script>
+setup({explicit_done: true});
+onload = () => {
+ // Clone the above tests for the following 'display' types:
+ let display = ['inline-block', 'grid', 'inline-grid', 'flex', 'inline-flex', 'flow-root' ];
+ let tests = document.querySelector('#tests');
+ display.forEach((display) => {
+ let t = tests.cloneNode(true);
+ [...t.children].forEach((child) => {
+ child.setAttribute("style", "display:"+display);
+ });
+ document.body.appendChild(t);
+ });
+ // Check that all of them return an offset relative the padding edge.
+ var i = 0;
+ document.querySelectorAll('.target').forEach((target) => {
+ test(() => {
+ assert_equals(target.offsetLeft, 10, 'offsetLeft');
+ assert_equals(target.offsetTop, 2, 'offsetTop');
+ }, 'container: ' + i);
+ i++;
+ });
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/offsetTopLeft-empty-inline-offset.html b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-empty-inline-offset.html
new file mode 100644
index 0000000000..85abbb7c01
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-empty-inline-offset.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#extensions-to-the-htmlelement-interface">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ .container {
+ position: relative;
+ width: 8em;
+ height: 7em;
+ padding: 1em;
+ }
+</style>
+<div class="container" style="writing-mode:horizontal-tb;">
+ offset
+ <br>offset<span class="target"></span><span>ref</span>
+</div>
+<div class="container" style="writing-mode:vertical-lr;">
+ offset
+ <br>offset<span class="target"></span><span>ref</span>
+</div>
+<div class="container" style="writing-mode:vertical-rl;">
+ offset
+ <br>offset<span class="target"></span><span>ref</span>
+</div>
+<div class="container" style="writing-mode:horizontal-tb; direction:rtl;">
+ offset
+ <br>offset<span class="target"></span><span>ref</span>
+</div>
+<div class="container" style="writing-mode:vertical-lr; direction:rtl;">
+ offset
+ <br>offset<span class="target"></span><span>ref</span>
+</div>
+<div class="container" style="writing-mode:vertical-rl; direction:rtl;">
+ offset
+ <br>offset<span class="target"></span><span>ref</span>
+</div>
+<script>
+var i = 0;
+document.querySelectorAll('span.target').forEach((target) => {
+ var ref = target.nextSibling;
+ test(() => {
+ assert_equals(target.offsetLeft, ref.offsetLeft, 'offsetLeft');
+ assert_equals(target.offsetTop, ref.offsetTop, 'offsetTop');
+ }, 'offsetTop/Left of empty inline elements should work as if they were not empty: ' + i);
+ i++;
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/offsetTopLeft-empty-inline.html b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-empty-inline.html
new file mode 100644
index 0000000000..d553cb150d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-empty-inline.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#extensions-to-the-htmlelement-interface">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ .container {
+ position: relative;
+ width: 8em;
+ height: 7em;
+ padding: 1em;
+ }
+</style>
+<div class="container" style="writing-mode:horizontal-tb;">
+ <br><span class="target"></span><span>ref</span>
+</div>
+<div class="container" style="writing-mode:vertical-lr;">
+ <br><span class="target"></span><span>ref</span>
+</div>
+<div class="container" style="writing-mode:vertical-rl;">
+ <br><span class="target"></span><span>ref</span>
+</div>
+<script>
+var i = 0;
+document.querySelectorAll('span.target').forEach((target) => {
+ var ref = target.nextSibling;
+ test(() => {
+ assert_equals(target.offsetLeft, ref.offsetLeft, 'offsetLeft');
+ assert_equals(target.offsetTop, ref.offsetTop, 'offsetTop');
+ }, 'offsetTop/Left of empty inline elements should work as if they were not empty: ' + i);
+ i++;
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/offsetTopLeft-inline.html b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-inline.html
new file mode 100644
index 0000000000..772cc34f05
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-inline.html
@@ -0,0 +1,54 @@
+<!DOCTYPE html>
+<link rel="author" title="Morten Stenshorne" href="mstensho@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#extensions-to-the-htmlelement-interface">
+<link rel="match" href="../reference/nothing.html">
+<style>
+ .container {
+ float: left;
+ width: 8em;
+ height: 7em;
+ padding: 1em;
+ color: red;
+ }
+ .correctionFluid {
+ position: absolute;
+ background: white;
+
+ /* Add some fluff to cover text ink-overflow. */
+ outline:2px solid white;
+ }
+</style>
+<p>There should be nothing below.</p>
+ <div class="container" style="writing-mode:horizontal-tb;">
+ <br><span class="child">FAIL</span>
+ </div>
+ <div class="container" style="writing-mode:vertical-lr;">
+ <br><span class="child">FAIL</span>
+ </div>
+ <div class="container" style="writing-mode:vertical-rl;">
+ <br><span class="child">FAIL</span>
+ </div>
+ <div class="container" style="writing-mode:horizontal-tb; direction:rtl;">
+ <br><span class="child">FAIL</span>
+ </div>
+ <div class="container" style="writing-mode:vertical-lr; direction:rtl;">
+ <br><span class="child">FAIL</span>
+ </div>
+ <div class="container" style="writing-mode:vertical-rl; direction:rtl;">
+ <br><span class="child">FAIL</span>
+ </div>
+<script>
+ // Create a white absolutely positioned box for each span.child
+ // element and cover it.
+
+ let elements = document.querySelectorAll("span.child");
+ elements.forEach((element)=> {
+ let correctionFluid = document.createElement("div");
+ correctionFluid.className = "correctionFluid";
+ correctionFluid.style.left = element.offsetLeft + "px";
+ correctionFluid.style.top = element.offsetTop + "px";
+ correctionFluid.style.width = element.offsetWidth + "px";
+ correctionFluid.style.height = element.offsetHeight + "px";
+ document.body.appendChild(correctionFluid);
+ });
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/offsetTopLeft-leading-space-inline.html b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-leading-space-inline.html
new file mode 100644
index 0000000000..a121cdf86a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-leading-space-inline.html
@@ -0,0 +1,32 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#extensions-to-the-htmlelement-interface">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ .container {
+ position: relative;
+ width: 8em;
+ height: 7em;
+ padding: 1em;
+ }
+</style>
+<div class="container" style="writing-mode:horizontal-tb;">
+ <br><span class="target"> </span><span>ref</span>
+</div>
+<div class="container" style="writing-mode:vertical-lr;">
+ <br><span class="target"> </span><span>ref</span>
+</div>
+<div class="container" style="writing-mode:vertical-rl;">
+ <br><span class="target"> </span><span>ref</span>
+</div>
+<script>
+var i = 0;
+document.querySelectorAll('span.target').forEach((target) => {
+ var ref = target.nextSibling;
+ test(() => {
+ assert_equals(target.offsetLeft, ref.offsetLeft, 'offsetLeft');
+ assert_equals(target.offsetTop, ref.offsetTop, 'offsetTop');
+ }, 'offsetTop/Left of empty inline elements should work as if they were not empty: ' + i);
+ i++;
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/offsetTopLeft-trailing-space-inline.html b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-trailing-space-inline.html
new file mode 100644
index 0000000000..3e50b78b9a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/offsetTopLeft-trailing-space-inline.html
@@ -0,0 +1,36 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#extensions-to-the-htmlelement-interface">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ .container {
+ position: relative;
+ width: 8em;
+ height: 7em;
+ padding: 1em;
+ }
+</style>
+<div class="container" style="writing-mode:horizontal-tb;">
+ <br><span>ref</span><span class="target"> </span>
+</div>
+<div class="container" style="writing-mode:vertical-lr;">
+ <br><span>ref</span><span class="target"> </span>
+</div>
+<div class="container" style="writing-mode:vertical-rl;">
+ <br><span>ref</span><span class="target"> </span>
+</div>
+<script>
+var i = 0;
+document.querySelectorAll('span.target').forEach((target) => {
+ var ref = target.previousSibling;
+ test(() => {
+ assert_equals(target.offsetLeft,
+ ref.offsetLeft + (i ? 0 : ref.offsetWidth),
+ 'offsetLeft');
+ assert_equals(target.offsetTop,
+ ref.offsetTop + (i ? ref.offsetHeight : 0),
+ 'offsetTop');
+ }, 'offsetTop/Left of empty inline elements should work as if they were not empty: ' + i);
+ i++;
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/offsetTopLeftInScrollableParent.html b/testing/web-platform/tests/css/cssom-view/offsetTopLeftInScrollableParent.html
new file mode 100644
index 0000000000..8f44337954
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/offsetTopLeftInScrollableParent.html
@@ -0,0 +1,111 @@
+<!doctype html>
+<meta charset=utf-8>
+<title></title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id=log></div>
+<div id="parent" style="overflow:scroll; height: 100px; position: relative">
+ <div id="spacer" style="height: 200px"></div>
+ <div id="child"></div>
+ <div id="absolute-child" style="position: absolute; top: 41px; left: 43px"></div>
+</div>
+<script>
+test(function() {
+ var child = document.getElementById("child");
+ assert_equals(child.offsetTop, 200, "Child is after spacer");
+ assert_equals(child.offsetLeft, 0, "Child is flush left");
+ var absChild = document.getElementById("absolute-child");
+ assert_equals(absChild.offsetTop, 41, "Abspos child is y-positioned");
+ assert_equals(absChild.offsetLeft, 43, "Abspos child is x-positioned");
+}, "Basic functionality");
+
+test(function() {
+ var parent = document.getElementById("parent");
+ parent.scrollTop = 100;
+ var child = document.getElementById("child");
+ assert_equals(child.offsetTop, 200, "Child is after spacer");
+ assert_equals(child.offsetLeft, 0, "Child is flush left");
+ var absChild = document.getElementById("absolute-child");
+ assert_equals(absChild.offsetTop, 41, "Abspos child is y-positioned");
+ assert_equals(absChild.offsetLeft, 43, "Abspos child is x-positioned");
+}, "Basic functionality in scrolled parent");
+
+test(function() {
+ var child = document.getElementById("child");
+ child.style.marginTop = "20px"
+ child.style.marginLeft = "100px";
+ assert_equals(child.offsetTop, 220, "Child is after spacer and margin");
+ assert_equals(child.offsetLeft, 100, "Child is 100px from left");
+ var absChild = document.getElementById("absolute-child");
+ absChild.style.marginTop = "20px"
+ absChild.style.marginLeft = "100px";
+ assert_equals(absChild.offsetTop, 61, "Abspos child is y-positioned and has margin");
+ assert_equals(absChild.offsetLeft, 143, "Abspos child is x-positioned and has margin");
+}, "Margins on child");
+
+test(function() {
+ var parent = document.getElementById("parent");
+ parent.style.marginTop = "66px"
+ parent.style.marginLeft = "33px";
+ var child = document.getElementById("child");
+ assert_equals(child.offsetTop, 220, "Child is after spacer and margin");
+ assert_equals(child.offsetLeft, 100, "Child is 100px from left");
+ var absChild = document.getElementById("absolute-child");
+ assert_equals(absChild.offsetTop, 61, "Abspos child is y-positioned and has margin");
+ assert_equals(absChild.offsetLeft, 143, "Abspos child is x-positioned and has margin");
+}, "Margins on child and parent");
+
+test(function() {
+ var child = document.getElementById("child");
+ child.style.borderTop = "13px solid green";
+ child.style.borderLeft = "7px solid green";
+ assert_equals(child.offsetTop, 220, "Child is after spacer and margin");
+ assert_equals(child.offsetLeft, 100, "Child is 100px from left");
+ var absChild = document.getElementById("absolute-child");
+ absChild.style.borderTop = "13px solid green";
+ absChild.style.borderLeft = "7px solid green";
+ assert_equals(absChild.offsetTop, 61, "Abspos child is y-positioned and has margin");
+ assert_equals(absChild.offsetLeft, 143, "Abspos child is x-positioned and has margin");
+}, "Margins on child and parent, border on child");
+
+test(function() {
+ var parent = document.getElementById("parent");
+ parent.style.borderTop = "23px solid yellow";
+ parent.style.borderLeft = "19px solid yellow";
+ var child = document.getElementById("child");
+ assert_equals(child.offsetTop, 220, "Child is after spacer and margin");
+ assert_equals(child.offsetLeft, 100, "Child is 100px from left");
+ var absChild = document.getElementById("absolute-child");
+ assert_equals(absChild.offsetTop, 61, "Abspos child is y-positioned and has margin");
+ assert_equals(absChild.offsetLeft, 143, "Abspos child is x-positioned and has margin");
+}, "Margins on child and parent, border on child and parent");
+
+
+test(function() {
+ var child = document.getElementById("child");
+ child.style.paddingTop = "31px";
+ child.style.paddingLeft = "37px";
+ assert_equals(child.offsetTop, 220, "Child is after spacer and margin");
+ assert_equals(child.offsetLeft, 100, "Child is 100px from left");
+ var absChild = document.getElementById("absolute-child");
+ absChild.style.paddingTop = "31px";
+ absChild.style.paddingLeft = "37px";
+ assert_equals(absChild.offsetTop, 61, "Abspos child is y-positioned and has margin");
+ assert_equals(absChild.offsetLeft, 143, "Abspos child is x-positioned and has margin");
+}, "Margins on child and parent, border on child and parent, padding on child");
+
+
+test(function() {
+ var parent = document.getElementById("parent");
+ parent.style.paddingTop = "31px";
+ parent.style.paddingLeft = "37px";
+ var child = document.getElementById("child");
+ assert_equals(child.offsetTop, 251, "Child is after spacer and margin and parent padding");
+ assert_equals(child.offsetLeft, 137, "Child is 100px + parent padding from left");
+ var absChild = document.getElementById("absolute-child");
+ // Padding on the parent does not affect the position of the absolute containing block.
+ assert_equals(absChild.offsetTop, 61, "Abspos child is y-positioned and has margin");
+ assert_equals(absChild.offsetLeft, 143, "Abspos child is x-positioned and has margin");
+}, "Margins on child and parent, border on child and parent, padding on child and parent");
+
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/outer-svg.html b/testing/web-platform/tests/css/cssom-view/outer-svg.html
new file mode 100644
index 0000000000..3b062c7548
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/outer-svg.html
@@ -0,0 +1,35 @@
+<!doctype html>
+<title>CSS Tests: client* and scroll* APIs work as expected with outer SVG elements</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=874811">
+<link rel="author" title="violet" href="mailto:violet.bugreport@gmail.com">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+#u {
+ padding: 30px;
+ transform: translate(50px,60px) scale(2,3);
+ border: 5px solid lime;
+ width: 50px;
+ height: 100px;
+}
+</style>
+<div style="width: 100px; height: 2000px; border: 1px solid blue"></div>
+<svg id="u"></svg>
+<script>
+let u = document.getElementById("u");
+test(function() {
+ assert_equals(u.clientTop, 5, "u.clientTop");
+ assert_equals(u.clientLeft, 5, "u.clientLeft");
+ assert_equals(u.clientWidth, 110, "u.clientWidth");
+ assert_equals(u.clientHeight, 160, "u.clientHeight");
+}, "clientWidth, clientHeight, clientTop and clientLeft work on outer svg element");
+test(function() {
+ assert_equals(u.scrollTop, 0, "u.scrollTop");
+ assert_equals(u.scrollLeft, 0, "u.scrollLeft");
+ assert_equals(u.scrollWidth, 110, "u.scrollWidth");
+ assert_equals(u.scrollHeight, 160, "u.scrollHeight");
+}, "scrollWidth, scrollHeight, scrollTop and scrollLeft work on outer svg element");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-computed.html b/testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-computed.html
new file mode 100644
index 0000000000..ffea289ea0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-computed.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSSOM View: getComputedStyle().scrollBehavior</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<meta name="assert" content="scroll-behavior computed value is as specified.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/computed-testcommon.js"></script>
+</head>
+<body>
+<div id="target"></div>
+<script>
+test_computed_value("scroll-behavior", 'auto');
+test_computed_value("scroll-behavior", 'smooth');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-invalid.html b/testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-invalid.html
new file mode 100644
index 0000000000..6dfe36e211
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-invalid.html
@@ -0,0 +1,19 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSSOM View: parsing scroll-behavior with invalid values</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<meta name="assert" content="scroll-behavior supports only the grammar 'auto | smooth'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_invalid_value("scroll-behavior", 'normal');
+test_invalid_value("scroll-behavior", 'auto smooth');
+test_invalid_value("scroll-behavior", 'auto, smooth');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-valid.html b/testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-valid.html
new file mode 100644
index 0000000000..6e708b9d81
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/parsing/scroll-behavior-valid.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<title>CSSOM View: parsing scroll-behavior with valid values</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<meta name="assert" content="scroll-behavior supports the full grammar 'auto | smooth'.">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/css/support/parsing-testcommon.js"></script>
+</head>
+<body>
+<script>
+test_valid_value("scroll-behavior", 'auto');
+test_valid_value("scroll-behavior", 'smooth');
+</script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/position-sticky-root-scroller-with-scroll-behavior.html b/testing/web-platform/tests/css/cssom-view/position-sticky-root-scroller-with-scroll-behavior.html
new file mode 100644
index 0000000000..e23c8235cb
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/position-sticky-root-scroller-with-scroll-behavior.html
@@ -0,0 +1,42 @@
+<!DOCTYPE html>
+<title>position:sticky should operate correctly for the root scroller</title>
+<link rel="help" href="https://drafts.csswg.org/css-position-3/#valdef-position-sticky">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<meta name="assert" content="This test checks that position:sticky elements work when using the root (document) scroller which has `scroll-behavior` property" />
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/scroll-behavior.js"></script>
+
+<style>
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ /* Assumption: 3000px is taller than any user agents test window size. */
+ height: 3000px;
+ /* A property which propagates for <html>. */
+ overflow-x: hidden;
+}
+
+#sticky {
+ position: sticky;
+ top: 50px;
+ width: 200px;
+ height: 200px;
+ background-color: green;
+}
+</style>
+
+<div id="sticky">This is a sticky element!</div>
+
+<script>
+promise_test(async () => {
+ window.scrollTo(0, 700);
+
+ await waitForScrollEnd(document.scrollingElement);
+
+ assert_equals(sticky.offsetTop, 700 + 50);
+}, 'Sticky elements work with the root (document) scroller');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/pt-to-px-width.html b/testing/web-platform/tests/css/cssom-view/pt-to-px-width.html
new file mode 100644
index 0000000000..485149ac74
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/pt-to-px-width.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>10pt converted to offset/client/scroll width</title>
+<link rel="help" href="https://www.w3.org/TR/css-values-4/#absolute-lengths">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+/* According to the CSS Values specification 10pt is abour 13.33px. */
+#target { width: 10pt; }
+</style>
+
+<div id="target"></div>
+
+<script>
+test(() => {
+ assert_equals(target.scrollWidth, 13, "scrollWidth");
+ assert_equals(target.offsetWidth, 13, "offsetWidth");
+ assert_equals(target.clientWidth, 13, "clientWidth");
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/range-bounding-client-rect-with-display-contents.html b/testing/web-platform/tests/css/cssom-view/range-bounding-client-rect-with-display-contents.html
new file mode 100644
index 0000000000..501a94e07f
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/range-bounding-client-rect-with-display-contents.html
@@ -0,0 +1,39 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>Include display:contents elements recursively when calculating bounding rect for a ranges</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view-1/#dom-range-getboundingclientrect">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="container">
+ <div id="spacerBefore">spacer before</div>
+ <div style="display:contents">
+ <div style="height:30px; background:lightblue">
+ HEIGHT: 30px
+ </div>
+ <div style="display:contents">
+ <div style="display:contents">
+ <div style="height:30px; background:lightblue">
+ HEIGHT: 30px
+ </div>
+ </div>
+ </div>
+ </div>
+ <div id="spacerAfter">spacer after</div>
+</div>
+<script>
+ test(function () {
+ const spacerBefore = document.getElementById("spacerBefore");
+ const spacerAfter = document.getElementById("spacerAfter");
+
+ const expected = spacerAfter.getBoundingClientRect().top - spacerBefore.getBoundingClientRect().bottom;
+
+ const rangeBetweenSpacers = document.createRange();
+ rangeBetweenSpacers.setStartAfter(spacerBefore);
+ rangeBetweenSpacers.setEndBefore(spacerAfter);
+
+ const actual = rangeBetweenSpacers.getBoundingClientRect().height;
+
+ assert_true(actual > 0, "range has vertical height");
+ assert_equals(expected, actual, "range.getBoundingClientRect().height");
+ }, "the space between elements using a range should be the same as using another method")
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/resize-event-on-initial-layout.html b/testing/web-platform/tests/css/cssom-view/resize-event-on-initial-layout.html
new file mode 100644
index 0000000000..dc2f04bcce
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/resize-event-on-initial-layout.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<meta name="viewport" content="width=device-width">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#run-the-resize-steps"/>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+promise_test(async t => {
+ let gotResizeEvent = false;
+
+ on_event(window, 'resize', () => gotResizeEvent = true);
+
+ await new Promise(resolve => requestAnimationFrame(resolve));
+ await new Promise(resolve => requestAnimationFrame(resolve));
+
+ assert_false(gotResizeEvent, 'resize event should not be fired');
+}, 'resize events are not fired on the initial layout');
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/resources/elementsFromPoint.js b/testing/web-platform/tests/css/cssom-view/resources/elementsFromPoint.js
new file mode 100644
index 0000000000..ba986ef3f5
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/resources/elementsFromPoint.js
@@ -0,0 +1,48 @@
+function nodeToString(node) {
+ var str = '';
+ if (node.nodeType == Node.ELEMENT_NODE) {
+ str += node.nodeName;
+ if (node.id)
+ str += '#' + node.id;
+ else if (node.class)
+ str += '.' + node.class;
+ } else if (node.nodeType == Node.TEXT_NODE) {
+ str += '\'' + node.data + '\'';
+ } else if (node.nodeType == Node.DOCUMENT_NODE) {
+ str += '#document';
+ }
+ return str;
+}
+
+function nodeListToString(nodes) {
+ var nodeString = '';
+
+ for (var i = 0; i < nodes.length; i++) {
+ var str = nodeToString(nodes[i]);
+ if (!str)
+ continue;
+ nodeString += str;
+ if (i + 1 < nodes.length)
+ nodeString += ', ';
+ }
+ return nodeString;
+}
+
+function assertElementsFromPoint(doc, x, y, expected) {
+ var query = doc + '.elementsFromPoint(' + x + ',' + y + ')';
+ var sequence = eval(query);
+ assert_equals(nodeListToString(sequence), nodeListToString(expected), query);
+}
+
+function checkElementsFromPointFourCorners(doc, element, expectedTopLeft, expectedTopRight, expectedBottomLeft, expectedBottomRight) {
+ var rect = eval(doc + '.getElementById(\'' + element + '\')').getBoundingClientRect();
+ var topLeft = {x: rect.left + 1, y: rect.top + 1};
+ var topRight = {x: rect.right - 1, y: rect.top + 1};
+ var bottomLeft = {x: rect.left + 1, y: rect.bottom - 1};
+ var bottomRight = {x: rect.right - 1, y: rect.bottom - 1};
+
+ assertElementsFromPoint(doc, topLeft.x, topLeft.y, expectedTopLeft);
+ assertElementsFromPoint(doc, topRight.x, topRight.y, expectedTopRight);
+ assertElementsFromPoint(doc, bottomLeft.x, bottomLeft.y, expectedBottomLeft);
+ assertElementsFromPoint(doc, bottomRight.x, bottomRight.y, expectedBottomRight);
+}
diff --git a/testing/web-platform/tests/css/cssom-view/resources/iframe1.html b/testing/web-platform/tests/css/cssom-view/resources/iframe1.html
new file mode 100644
index 0000000000..ec4699465d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/resources/iframe1.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<style>
+html, body {
+ margin: 0;
+ padding: 0;
+}
+#div {
+ width: 100px;
+ height: 100px;
+ background: red;
+}
+</style>
+<div id='div'></div>
+<script>
+window.onload = window.parent.onFrameLoaded();
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/resources/iframe2.html b/testing/web-platform/tests/css/cssom-view/resources/iframe2.html
new file mode 100644
index 0000000000..7bb944c9d5
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/resources/iframe2.html
@@ -0,0 +1,25 @@
+<!DOCTYPE HTML>
+<style>
+html, body {
+ margin: 0;
+ padding: 0;
+}
+#big {
+ width: 125px;
+ height: 500px;
+ background: blue;
+}
+#small {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100px;
+ height: 100px;
+ background: green;
+}
+</style>
+<div id='big'></div>
+<div id='small'></div>
+<script>
+window.onload = window.parent.onFrameLoaded();
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/resources/matchMedia.js b/testing/web-platform/tests/css/cssom-view/resources/matchMedia.js
new file mode 100644
index 0000000000..f8947e0472
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/resources/matchMedia.js
@@ -0,0 +1,60 @@
+"use strict";
+
+{
+// private variables are defined with `const` so they don't leak outside this block statement
+const IFRAME_DEFAULT_SIZE = "200";
+const iframes = new WeakMap();
+
+// helpers are defined with `var` so they are globally accessible
+var createMQL = async t => {
+ const iframe = await createIFrame(t);
+ const mql = iframe.contentWindow.matchMedia(`(max-width: ${IFRAME_DEFAULT_SIZE}px)`);
+ assert_true(mql.matches, "MQL should match on newly created <iframe>");
+ iframes.set(mql, iframe);
+ return mql;
+};
+
+var createIFrame = (t, width = IFRAME_DEFAULT_SIZE, height = width) => {
+ assert_not_equals(document.body, null, "<body> element is missing");
+
+ const iframe = document.createElement("iframe");
+ iframe.srcdoc = "";
+ iframe.width = String(width);
+ iframe.height = String(height);
+ iframe.style.border = "none";
+
+ t.add_cleanup(() => {
+ document.body.removeChild(iframe);
+ });
+
+ return new Promise(resolve => {
+ iframe.addEventListener("load", () => {
+ iframe.contentDocument.body.offsetWidth; // reflow
+ resolve(iframe);
+ });
+
+ document.body.appendChild(iframe);
+ });
+};
+
+var triggerMQLEvent = mql => {
+ const iframe = iframes.get(mql);
+ assert_not_equals(iframe, undefined, "Passed MQL instance was not created with createMQL");
+ iframe.width = iframe.width === IFRAME_DEFAULT_SIZE ? "250" : IFRAME_DEFAULT_SIZE;
+};
+
+var getWindow = mql => {
+ const iframe = iframes.get(mql);
+ assert_not_equals(iframe, undefined, "Passed MQL instance was not created with createMQL");
+ return iframe.contentWindow;
+};
+
+var waitForChangesReported = () => {
+ return new Promise(resolve => {
+ requestAnimationFrame(() => {
+ requestAnimationFrame(resolve);
+ });
+ });
+};
+
+}
diff --git a/testing/web-platform/tests/css/cssom-view/screenLeftTop.html b/testing/web-platform/tests/css/cssom-view/screenLeftTop.html
new file mode 100644
index 0000000000..58d868866e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/screenLeftTop.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-window-screenleft">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-window-screentop">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+test(() => {
+ assert_equals(typeof window.screenLeft, "number", "screenLeft type");
+ assert_equals(window.screenLeft, window.screenX, "alias of screenX");
+}, "screenLeft");
+
+test(() => {
+ assert_equals(typeof window.screenTop, "number", "screenTop type");
+ assert_equals(window.screenTop, window.screenY, "alias of screenY");
+}, "screenTop");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-back-to-initial-position.html b/testing/web-platform/tests/css/cssom-view/scroll-back-to-initial-position.html
new file mode 100644
index 0000000000..8ccb499dd3
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-back-to-initial-position.html
@@ -0,0 +1,44 @@
+<!DOCTYPE html>
+<title>Scroll back to initial position</title>
+<meta name="timeout" content="long">
+<link rel="author" title="Yupei Wang" href="mailto:perryuwang@tencent.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#smooth-scrolling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/scroll-behavior.js"></script>
+<style>
+ .filler { height: 1000px }
+
+ #scrollable {
+ overflow: scroll;
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div id="log"></div>
+<div id="testContainer">
+ <div id="scrollable">
+ <div class="filler"></div>
+ </div>
+</div>
+<script>
+ promise_test(() => {
+ resetScroll(scrollable);
+ assert_equals(scrollable.scrollTop, 0, "Inital value of scrollTop");
+ scrollNode(scrollable, "scroll", "smooth", 0, 500);
+ scrollNode(scrollable, "scroll", "smooth", 0, 0);
+ return waitForScrollEnd(scrollable).then(() => {
+ assert_equals(scrollable.scrollTop, 0, "Final value of scrollTop");
+ });
+ }, "Element should scroll back to initial position with smooth behavior");
+
+ promise_test(() => {
+ resetScroll(scrollable);
+ assert_equals(scrollable.scrollTop, 0, "Inital value of scrollTop");
+ scrollNode(scrollable, "scroll", "smooth", 0, 500);
+ scrollNode(scrollable, "scroll", "auto", 0, 0);
+ return waitForScrollEnd(scrollable).then(() => {
+ assert_equals(scrollable.scrollTop, 0, "Final value of scrollTop");
+ });
+ }, "Element should scroll back to initial position with auto behavior");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-behavior-default-css.html b/testing/web-platform/tests/css/cssom-view/scroll-behavior-default-css.html
new file mode 100644
index 0000000000..8606b1f3ed
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-behavior-default-css.html
@@ -0,0 +1,52 @@
+<!DOCTYPE html>
+<title>Testing default value of scroll-behavior</title>
+<meta name="timeout" content="long"/>
+<link rel="author" title="Frédéric Wang" href="mailto:fwang@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-box">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/scroll-behavior.js"></script>
+<style>
+ .scrollable {
+ overflow: auto;
+ width: 400px;
+ height: 200px;
+ }
+</style>
+<div id="log">
+</div>
+<div id="overflowNode" class="scrollable">
+ <div style="width: 2000px; height: 1000px; background: linear-gradient(135deg, red, green);">
+ <span style="display: inline-block; width: 500px; height: 250px;"></span><span id="elementToReveal" style="display: inline-block; vertical-align: -15px; width: 10px; height: 15px; background: black;"></span>
+ </div>
+</div>
+<script>
+ var scrollingElement = overflowNode;
+ var elementToRevealLeft = 500;
+ var elementToRevealTop = 250;
+ var scrollFunction = "scroll";
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, "scroll", "instant", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, "Instant scrolling of an element with default scroll-behavior");
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, "scroll", "smooth", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, "Smooth scrolling of an element with default scroll-behavior");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-behavior-element.html b/testing/web-platform/tests/css/cssom-view/scroll-behavior-element.html
new file mode 100644
index 0000000000..c598d97b33
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-behavior-element.html
@@ -0,0 +1,189 @@
+<!DOCTYPE html>
+<title>Testing scrollOptions' behavior for Element.scroll* and scroll-behavior on an element</title>
+<meta name="timeout" content="long"/>
+<link rel="author" title="Frédéric Wang" href="mailto:fwang@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-box">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/scroll-behavior.js"></script>
+<style>
+ .scrollable {
+ overflow: auto;
+ width: 400px;
+ height: 200px;
+ }
+ .autoBehavior {
+ scroll-behavior: auto;
+ }
+ .smoothBehavior {
+ scroll-behavior: smooth;
+ }
+</style>
+<div id="log">
+</div>
+<div id="overflowNode" class="scrollable">
+ <div style="width: 2000px; height: 1000px; background: linear-gradient(135deg, red, green);">
+ <span style="display: inline-block; width: 500px; height: 250px;"></span><span id="elementToReveal" style="display: inline-block; vertical-align: -15px; width: 10px; height: 15px; background: black;"></span>
+ </div>
+</div>
+<script>
+ var scrollingElement = overflowNode;
+ var styledElement = overflowNode;
+ var elementToRevealLeft = 500;
+ var elementToRevealTop = 250;
+
+ ["scroll", "scrollTo", "scrollBy", "scrollIntoView"].forEach((scrollFunction) => {
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, null, elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Element with auto scroll-behavior ; ${scrollFunction}() with default behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "auto", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Element with auto scroll-behavior ; ${scrollFunction}() with auto behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "instant", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Element with auto scroll-behavior ; ${scrollFunction}() with instant behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "smooth", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Element with auto scroll-behavior ; ${scrollFunction}() with smooth behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, null, elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Element with smooth scroll-behavior ; ${scrollFunction}() with default behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "auto", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Element with smooth scroll-behavior ; ${scrollFunction}() with auto behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "instant", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Element with smooth scroll-behavior ; ${scrollFunction}() with instant behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "smooth", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Element with smooth scroll-behavior ; ${scrollFunction}() with smooth behavior`);
+ });
+
+ [{scrollAttribute: "scrollLeft", scrollValue: elementToRevealLeft}, {scrollAttribute: "scrollTop", scrollValue: elementToRevealTop}].forEach((attributeTest) => {
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ var expectedValue = Number(attributeTest.scrollValue);
+ scrollingElement[attributeTest.scrollAttribute] = expectedValue;
+ assert_equals( scrollingElement[attributeTest.scrollAttribute], expectedValue, "Should set scroll attribute immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Set ${attributeTest.scrollAttribute} to element with auto scroll-behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ var expectedValue = Number(attributeTest.scrollValue);
+ scrollingElement[attributeTest.scrollAttribute] = expectedValue;
+ assert_less_than(scrollingElement[attributeTest.scrollAttribute], expectedValue, "Shouldn't set scroll attribute immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement[attributeTest.scrollAttribute], expectedValue, "Final value of scroll attribute");
+ });
+ }, `Set ${attributeTest.scrollAttribute} to element with smooth scroll-behavior`);
+ });
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, "scroll", "smooth", elementToRevealLeft, elementToRevealTop);
+ scrollNode(scrollingElement, "scroll", "smooth", elementToRevealLeft / 2, elementToRevealTop / 2);
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft / 2, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop / 2, "Final value of scrollTop");
+ });
+ }, "Aborting an ongoing smooth scrolling on an element with another smooth scrolling");
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, "scroll", "smooth", elementToRevealLeft, elementToRevealTop);
+ scrollNode(scrollingElement, "scroll", "instant", 0, 0);
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, 0, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, 0, "Final value of scrollTop");
+ });
+ }, "Aborting an ongoing smooth scrolling on an element with an instant scrolling");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-behavior-main-frame-root.html b/testing/web-platform/tests/css/cssom-view/scroll-behavior-main-frame-root.html
new file mode 100644
index 0000000000..7b4a8e3863
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-behavior-main-frame-root.html
@@ -0,0 +1,196 @@
+<!DOCTYPE html>
+<title>Testing scrollOptions' behavior for Element.scroll* and scroll-behavior on the root of the main frame</title>
+<meta name="timeout" content="long"/>
+<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
+<link rel="author" title="Frédéric Wang" href="mailto:fwang@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-box">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/scroll-behavior.js"></script>
+<style>
+ body {
+ margin: 0;
+ }
+ .autoBehavior {
+ scroll-behavior: auto;
+ }
+ .smoothBehavior {
+ scroll-behavior: smooth;
+ }
+</style>
+<div id="log">
+</div>
+<div id="pageContent" style="position: absolute; left: 0; top: 0;">
+ <div id="elementToReveal" style="position: absolute; display: inline-block; width: 10px; height: 15px; background: black;"></div>
+</div>
+<script>
+ var pageLoaded = async_test("Page loaded");
+ var scrollingElement, styledElement, elementToRevealLeft, elementToRevealTop;
+ window.addEventListener("load", pageLoaded.step_func_done(function() {
+ scrollingElement = document.scrollingElement;
+ styledElement = document.documentElement;
+ pageContent.style.width = (10 + window.innerWidth) * 5 + "px";
+ pageContent.style.height = (20 + window.innerHeight) * 6 + "px";
+ elementToRevealLeft = (10 + window.innerWidth) * 3;
+ elementToRevealTop = (20 + window.innerHeight) * 4;
+ elementToReveal.style.left = elementToRevealLeft + "px";
+ elementToReveal.style.top = elementToRevealTop + "px";
+
+ add_completion_callback(() => { resetScroll(scrollingElement); });
+
+ ["scroll", "scrollTo", "scrollBy", "scrollIntoView"].forEach((scrollFunction) => {
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, null, elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with default behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "auto", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with auto behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "instant", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with instant behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "smooth", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with smooth behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, null, elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with default behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "auto", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with auto behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "instant", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with instant behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "smooth", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with smooth behavior`);
+ });
+
+ [{scrollAttribute: "scrollLeft", scrollValue: elementToRevealLeft}, {scrollAttribute: "scrollTop", scrollValue: elementToRevealTop}].forEach((attributeTest) => {
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ var expectedValue = Number(attributeTest.scrollValue);
+ scrollingElement[attributeTest.scrollAttribute] = expectedValue;
+ assert_equals( scrollingElement[attributeTest.scrollAttribute], expectedValue, "Should set scroll attribute immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Set ${attributeTest.scrollAttribute} to frame with auto scroll-behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ var expectedValue = Number(attributeTest.scrollValue);
+ scrollingElement[attributeTest.scrollAttribute] = expectedValue;
+ assert_less_than(scrollingElement[attributeTest.scrollAttribute], expectedValue, "Shouldn't set scroll attribute immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement[attributeTest.scrollAttribute], expectedValue, "Final value of scroll attribute");
+ });
+ }, `Set ${attributeTest.scrollAttribute} to frame with smooth scroll-behavior`);
+ });
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, "scroll", "smooth", elementToRevealLeft, elementToRevealTop);
+ scrollNode(scrollingElement, "scroll", "smooth", elementToRevealLeft / 2, elementToRevealTop / 2);
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft / 2, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop / 2, "Final value of scrollTop");
+ });
+ }, "Aborting an ongoing smooth scrolling on the main frame with another smooth scrolling");
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, "scroll", "smooth", elementToRevealLeft, elementToRevealTop);
+ scrollNode(scrollingElement, "scroll", "instant", 0, 0);
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, 0, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, 0, "Final value of scrollTop");
+ });
+ }, "Aborting an ongoing smooth scrolling on the main frame with an instant scrolling");
+ }));
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-behavior-main-frame-window.html b/testing/web-platform/tests/css/cssom-view/scroll-behavior-main-frame-window.html
new file mode 100644
index 0000000000..8024d5319b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-behavior-main-frame-window.html
@@ -0,0 +1,170 @@
+<!DOCTYPE html>
+<title>Testing scrollOptions' behavior for Element.scroll* on the window of the main frame</title>
+<meta name="timeout" content="long"/>
+<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
+<link rel="author" title="Frédéric Wang" href="mailto:fwang@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-box">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/scroll-behavior.js"></script>
+<style>
+ body {
+ margin: 0;
+ }
+ .autoBehavior {
+ scroll-behavior: auto;
+ }
+ .smoothBehavior {
+ scroll-behavior: smooth;
+ }
+</style>
+<div id="log">
+</div>
+<div id="pageContent" style="position: absolute; left: 0; top: 0;">
+ <div id="elementToReveal" style="position: absolute; display: inline-block; width: 10px; height: 15px; background: black;"></div>
+</div>
+<script>
+ var pageLoaded = async_test("Page loaded");
+ var scrollingWindow, styledElement, elementToRevealLeft, elementToRevealTop;
+ window.addEventListener("load", pageLoaded.step_func_done(function() {
+ scrollingWindow = window;
+ styledElement = document.documentElement;
+ pageContent.style.width = (10 + window.innerWidth) * 5 + "px";
+ pageContent.style.height = (20 + window.innerHeight) * 6 + "px";
+ elementToRevealLeft = (10 + window.innerWidth) * 3;
+ elementToRevealTop = (20 + window.innerHeight) * 4;
+ elementToReveal.style.left = elementToRevealLeft + "px";
+ elementToReveal.style.top = elementToRevealTop + "px";
+
+ add_completion_callback(() => { resetScrollForWindow(window); });
+
+ ["scroll", "scrollTo", "scrollBy"].forEach((scrollFunction) => {
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, null, elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with default behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "auto", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with auto behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "instant", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with instant behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "smooth", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingWindow.scrollX, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingWindow.scrollY, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with smooth behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, null, elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingWindow.scrollX, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingWindow.scrollY, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with default behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "auto", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingWindow.scrollX, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingWindow.scrollY, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with auto behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "instant", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with instant behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "smooth", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingWindow.scrollX, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingWindow.scrollY, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with smooth behavior`);
+ });
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, "scroll", "smooth", elementToRevealLeft, elementToRevealTop);
+ scrollWindow(scrollingWindow, "scroll", "smooth", elementToRevealLeft / 2, elementToRevealTop / 2);
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft / 2, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop / 2, "Final value of scrollTop");
+ });
+ }, "Aborting an ongoing smooth scrolling on the main frame with another smooth scrolling");
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, "scroll", "smooth", elementToRevealLeft, elementToRevealTop);
+ scrollWindow(scrollingWindow, "scroll", "instant", 0, 0);
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, 0, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, 0, "Final value of scrollTop");
+ });
+ }, "Aborting an ongoing smooth scrolling on the main frame with an instant scrolling");
+ }));
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-behavior-scrollintoview-nested.html b/testing/web-platform/tests/css/cssom-view/scroll-behavior-scrollintoview-nested.html
new file mode 100644
index 0000000000..2a97e06566
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-behavior-scrollintoview-nested.html
@@ -0,0 +1,88 @@
+<!DOCTYPE html>
+<title>Testing scrollOptions' behavior with scrollIntoView for nested scrolling nodes</title>
+<meta name="timeout" content="long"/>
+<link rel="author" title="Frédéric Wang" href="mailto:fwang@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-box">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/scroll-behavior.js"></script>
+<style>
+ .scrollable {
+ overflow: auto;
+ height: 200px;
+ }
+ .smoothBehavior {
+ scroll-behavior: smooth;
+ }
+ .gradient {
+ background: linear-gradient(135deg, red, green);
+ }
+</style>
+<div id="log">
+</div>
+<div>
+ <div class="scrollable smoothBehavior" style="width: 450px">
+ <div class="gradient" style="width: 100px; height: 500px;"></div>
+ <div class="scrollable smoothBehavior" style="width: 400px">
+ <div class="gradient" style="width: 100px; height: 500px;"></div>
+ <div class="scrollable" style="width: 350px">
+ <div class="gradient" style="width: 100px; height: 500px;"></div>
+ <div class="scrollable" style="width: 300px">
+ <div class="gradient" style="width: 100px; height: 500px;"></div>
+ <div class="scrollable smoothBehavior" style="width: 250px">
+ <div class="gradient" style="width: 100px; height: 500px;"></div>
+ <div class="scrollable" style="width: 200px">
+ <div class="gradient" style="width: 100px; height: 500px;"></div>
+ <div id="elementToReveal" style="width: 10px; height: 10px; background: black;"></div>
+ <div class="gradient" style="width: 100px; height: 500px;"></div>
+ </div>
+ <div class="gradient" style="width: 100px; height: 500px;"></div>
+ </div>
+ <div class="gradient" style="width: 100px; height: 500px;"></div>
+ </div>
+ <div class="gradient" style="width: 100px; height: 500px;"></div>
+ </div>
+ <div class="gradient" style="width: 100px; height: 500px;"></div>
+ </div>
+ </div>
+</div>
+<script>
+ // The CSSOM-View spec and implementations follow different algorithms (scrolls performed in parallel, as inner-to-outer sequence or as outer-to-inner sequence).
+ // See https://github.com/w3c/csswg-drafts/issues/3127
+ promise_test(() => {
+ return new Promise(function(resolve, reject) {
+ var divs = document.querySelectorAll(".scrollable");
+ divs.forEach((scrollableDiv) => {
+ resetScroll(scrollableDiv);
+ });
+ elementToReveal.scrollIntoView({inline: "start", block: "start", behavior: "auto"});
+ var scrollTop = new Map();
+ var isSmooth = new Map();
+ divs.forEach((scrollableDiv) => {
+ scrollTop.set(scrollableDiv, scrollableDiv.scrollTop);
+ isSmooth.set(scrollableDiv, scrollableDiv.classList.contains("smoothBehavior"));
+ // If scroll operations are not performed in parallel, scroll boxes with instant behavior might also need to wait for their predecessors.
+ if (isSmooth.get(scrollableDiv))
+ assert_less_than(scrollTop.get(scrollableDiv), 500, "Element with smooth behavior should not scroll immediately");
+ });
+
+ observeScrolling(Array.from(divs), function(done) {
+ try {
+ divs.forEach((scrollableDiv) => {
+ assert_less_than_equal(scrollTop.get(scrollableDiv), scrollableDiv.scrollTop, "ScrollTop keeps increasing");
+ if (!isSmooth.get(scrollableDiv))
+ assert_any(assert_equals, scrollableDiv.scrollTop, [0, 500], "Element with instant behavior should jump to the final position");
+ if (done)
+ assert_equals(scrollableDiv.scrollTop, 500, "Final value of scrollTop");
+ scrollTop.set(scrollableDiv, scrollableDiv.scrollTop);
+ });
+ } catch(e) {
+ reject(e);
+ }
+ if (done)
+ resolve();
+ });
+ });
+ }, "scrollIntoView with nested elements with different scroll-behavior");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth-navigation.html b/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth-navigation.html
new file mode 100644
index 0000000000..299fd76976
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth-navigation.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<title>cssom-view - scroll-behavior: smooth</title>
+<meta name="timeout" content="long">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#smooth-scrolling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ .filler { height: 10000px }
+
+ .smooth {
+ scroll-behavior: smooth;
+ }
+
+ #scrollable {
+ overflow: scroll;
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div id="testContainer">
+ <div id="scrollable">
+ <div class="filler"></div>
+ </div>
+ <div class="filler"></div>
+</div>
+<script>
+ var instantHistoryNavigationTest =
+ async_test("Instant scrolling while doing history navigation.");
+ var smoothHistoryNavigationTest =
+ async_test("Smooth scrolling while doing history navigation.");
+
+ function instant() {
+ document.documentElement.className = "";
+ document.body.className = "";
+ window.scrollTo(0, 0);
+ var p = document.createElement("pre");
+ p.textContent = new Array(1000).join("newline\n");
+ var a = document.createElement("a");
+ a.href = "#";
+ a.name = "foo";
+ a.textContent = "foo";
+ p.appendChild(a);
+ document.body.appendChild(p);
+ window.onhashchange = function() {
+ window.onhashchange = function() {
+ instantHistoryNavigationTest.step(function() {
+ assert_equals(location.hash, "", "Shouldn't be scrolled to a fragment.");
+ assert_equals(window.scrollY, 0, "Shouldn't be scrolled back to top yet.");
+ });
+ p.remove();
+ instantHistoryNavigationTest.done();
+ smooth();
+ }
+
+ instantHistoryNavigationTest.step(function() {
+ assert_equals(location.hash, "#foo", "Should be scrolled to a fragment.");
+ assert_not_equals(window.scrollY, 0, "Shouldn't be scrolled to top anymore.");
+ });
+ history.back();
+ }
+
+ instantHistoryNavigationTest.step(function() {
+ assert_equals(window.scrollY, 0, "Should be scrolled to top.");
+ assert_equals(location.hash, "", "Shouldn't be scrolled to a fragment.");
+ });
+ location.hash = "foo";
+ };
+ instant();
+
+ function smooth() {
+ document.documentElement.className = "";
+ document.body.className = "";
+ window.scrollTo(0, 0);
+ var p = document.createElement("pre");
+ p.textContent = new Array(1000).join("newline\n");
+ var a = document.createElement("a");
+ a.href = "#";
+ a.name = "bar";
+ a.textContent = "bar";
+ p.appendChild(a);
+ document.body.appendChild(p);
+ window.onhashchange = function() {
+ window.onhashchange = function() {
+ smoothHistoryNavigationTest.step(function() {
+ assert_equals(location.hash, "", "Shouldn't be scrolled to a fragment.");
+ assert_not_equals(window.scrollY, 0, "Shouldn't be scrolled back to top yet.");
+ });
+ p.remove();
+ smoothHistoryNavigationTest.done();
+ }
+
+ smoothHistoryNavigationTest.step(function() {
+ assert_equals(location.hash, "#bar", "Should be scrolled to a fragment.");
+ assert_not_equals(window.scrollY, 0, "Shouldn't be scrolled to top anymore.");
+ });
+ history.back();
+ }
+
+ smoothHistoryNavigationTest.step(function() {
+ assert_equals(window.scrollY, 0, "Should be scrolled to top.");
+ assert_equals(location.hash, "", "Shouldn't be scrolled to a fragment.");
+ });
+ location.hash = "bar";
+ document.documentElement.className = "smooth";
+ };
+
+ testContainer.style.display = "none";
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth-positions.html b/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth-positions.html
new file mode 100644
index 0000000000..97905bb708
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth-positions.html
@@ -0,0 +1,179 @@
+<!DOCTYPE html>
+<title>Testing scroll positions when scrolling an element with smooth behavior</title>
+<meta name="timeout" content="long"/>
+<link rel="author" title="Frédéric Wang" href="mailto:fwang@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-box">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/scroll-behavior.js"></script>
+<style>
+ .scrollable {
+ overflow: auto;
+ width: 400px;
+ height: 200px;
+ scroll-behavior: smooth;
+ }
+</style>
+<div id="log">
+</div>
+<div id="overflowNode" class="scrollable">
+ <div style="width: 2000px; height: 1000px; background: linear-gradient(135deg, red, green);">
+ <span style="display: inline-block; width: 500px; height: 250px;"></span><span id="elementToReveal" style="display: inline-block; vertical-align: -15px; width: 10px; height: 15px; background: black;"></span>
+ </div>
+</div>
+<script>
+ // For smooth behavior, evolution of scroll positions over time is not specified by CSSOM View.
+ // This test relies on the minimal assumption that scroll position functions are monotonic.
+ ["scroll", "scrollTo", "scrollBy", "scrollIntoView"].forEach(function(scrollFunction) {
+ [{left:0, top:0}, {left:1000, top:0}, {left:0, top:500}, {left:1000, top:500}].forEach((initial) => {
+ var finalLeft = 500;
+ var finalTop = 250;
+ promise_test(() => {
+ return new Promise(function(resolve, reject) {
+ scrollNode(overflowNode, "scroll", "instant", initial.left, initial.top);
+ var oldLeft = overflowNode.scrollLeft;
+ var oldTop = overflowNode.scrollTop;
+ assert_equals(oldLeft, initial.left, "ScrollLeft should be at initial position");
+ assert_equals(oldTop, initial.top, "ScrollTop should be at initial position");
+ if (scrollFunction === "scrollBy")
+ scrollNode(overflowNode, scrollFunction, "smooth", finalLeft - initial.left, finalTop - initial.top);
+ else
+ scrollNode(overflowNode, scrollFunction, "smooth", finalLeft, finalTop);
+ observeScrolling(overflowNode, function(done) {
+ try {
+ var newLeft = overflowNode.scrollLeft;
+ var newTop = overflowNode.scrollTop;
+ assert_less_than_equal(Math.hypot(finalLeft - newLeft, finalTop - newTop), Math.hypot(finalLeft - oldLeft, finalTop - oldTop), "Scroll position should move towards the final position");
+ if (done) {
+ assert_equals(newLeft, finalLeft, "ScrollLeft should reach final position");
+ assert_equals(newTop, finalTop, "ScrollTop should reach final position");
+ }
+ oldLeft = newLeft;
+ oldTop = newTop;
+ } catch(e) {
+ reject(e);
+ }
+ if (done)
+ resolve();
+ });
+ });
+ }, `Scroll positions when performing smooth scrolling from (${initial.left}, ${initial.top}) to (${finalLeft}, ${finalTop}) using ${scrollFunction}() `);
+ });
+ });
+
+ [{scrollAttribute: "scrollLeft", scrollValue: 500}, {scrollAttribute: "scrollTop", scrollValue: 250}].forEach(function(scrollTest) {
+ var initialPosition = Number(scrollTest.scrollValue) * 2;
+ [0, initialPosition].forEach((initial) => {
+ promise_test(() => {
+ return new Promise(function(resolve, reject) {
+ scrollNode(overflowNode, "scroll", "instant", initial, initial);
+ var oldValue = overflowNode[scrollTest.scrollAttribute];
+ assert_equals(oldValue, initial, `${scrollTest.scrollAttribute} should be at initial position`);
+ var expectedValue = Number(scrollTest.scrollValue);
+ overflowNode[scrollTest.scrollAttribute] = expectedValue;
+ observeScrolling(overflowNode, function(done) {
+ try {
+ var newValue = overflowNode[scrollTest.scrollAttribute];
+ assert_less_than_equal(Math.abs(expectedValue - newValue), Math.abs(expectedValue - oldValue), "Scroll position should move towards the final position");
+ if (done)
+ assert_equals(newValue, expectedValue, `${scrollTest.scrollAttribute} should reach final position`);
+ oldValue = newValue;
+ } catch(e) {
+ reject(e);
+ }
+ if (done)
+ resolve();
+ });
+ });
+ }, `Scroll positions when performing smooth scrolling from ${initial} to ${scrollTest.scrollValue} by setting ${scrollTest.scrollAttribute} `);
+ });
+ });
+
+ promise_test(() => {
+ return new Promise(function(resolve, reject) {
+ resetScroll(overflowNode);
+ var initialScrollAborted = false;
+ var scrollDirectionChanged = false;
+ var oldLeft = overflowNode.scrollLeft;
+ var oldTop = overflowNode.scrollTop;
+ assert_equals(oldLeft, 0);
+ assert_equals(oldTop, 0);
+ scrollNode(overflowNode, "scroll", "smooth", 1500, 750);
+ observeScrolling(overflowNode, function(done) {
+ try {
+ var newLeft = overflowNode.scrollLeft;
+ var newTop = overflowNode.scrollTop;
+ if (initialScrollAborted) {
+ if (scrollDirectionChanged) {
+ assert_greater_than_equal(oldLeft, newLeft, "ScrollLeft keeps decreasing");
+ assert_greater_than_equal(oldTop, newTop, "ScrollTop keeps decreasing");
+ } else
+ scrollDirectionChanged = newLeft <= oldLeft && newTop <= oldTop;
+ } else {
+ assert_less_than_equal(oldLeft, newLeft, "ScrollLeft keeps increasing");
+ assert_less_than_equal(oldTop, newTop, "ScrollTop keeps increasing");
+ if (newLeft > 1000 && newTop > 500) {
+ // Abort the initial scroll.
+ initialScrollAborted = true;
+ scrollNode(overflowNode, "scroll", "smooth", 500, 250);
+ newLeft = overflowNode.scrollLeft;
+ newTop = overflowNode.scrollTop;
+ }
+ }
+ if (done) {
+ assert_equals(newLeft, 500, "ScrollLeft should reach final position");
+ assert_equals(newTop, 250, "ScrollTop should reach final position");
+ }
+ oldLeft = newLeft;
+ oldTop = newTop;
+ } catch(e) {
+ reject(e);
+ }
+ if (done)
+ resolve();
+ });
+ });
+ }, "Scroll positions when aborting a smooth scrolling with another smooth scrolling");
+
+ promise_test(() => {
+ return new Promise(function(resolve, reject) {
+ resetScroll(overflowNode);
+ var initialScrollAborted = false;
+ var oldLeft = overflowNode.scrollLeft;
+ var oldTop = overflowNode.scrollTop;
+ assert_equals(oldLeft, 0);
+ assert_equals(oldTop, 0);
+ scrollNode(overflowNode, "scroll", "smooth", 1500, 750);
+ observeScrolling(overflowNode, function(done) {
+ try {
+ var newLeft = overflowNode.scrollLeft;
+ var newTop = overflowNode.scrollTop;
+ if (!initialScrollAborted) {
+ assert_less_than_equal(oldLeft, newLeft, "ScrollLeft keeps increasing");
+ assert_less_than_equal(oldTop, newTop, "ScrollTop keeps increasing");
+ if (newLeft > 1000 && newTop > 500) {
+ // Abort the initial scroll.
+ initialScrollAborted = true;
+ scrollNode(overflowNode, "scroll", "instant", 500, 250);
+ newLeft = overflowNode.scrollLeft;
+ newTop = overflowNode.scrollTop;
+ assert_equals(newLeft, 500, "ScrollLeft should reach final position");
+ assert_equals(newTop, 250, "ScrollTop should reach final position");
+ }
+ }
+ if (done) {
+ assert_equals(newLeft, 500, "ScrollLeft should stay at final position");
+ assert_equals(newTop, 250, "ScrollTop should stay at final position");
+ }
+ oldLeft = newLeft;
+ oldTop = newTop;
+ } catch(e) {
+ reject(e);
+ }
+ if (done)
+ resolve();
+ });
+ });
+ }, "Scroll positions when aborting a smooth scrolling with an instant scrolling");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth.html b/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth.html
new file mode 100644
index 0000000000..cb2dd9e48a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-behavior-smooth.html
@@ -0,0 +1,61 @@
+<!DOCTYPE html>
+<title>cssom-view - scroll-behavior: smooth</title>
+<meta name="timeout" content="long">
+<link rel="author" title="Rune Lillesveen" href="mailto:futhark@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#smooth-scrolling">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ .filler { height: 10000px }
+
+ .smooth {
+ scroll-behavior: smooth;
+ }
+
+ #scrollable {
+ overflow: scroll;
+ width: 100px;
+ height: 100px;
+ }
+</style>
+<div id="testContainer">
+ <div id="scrollable">
+ <div class="filler"></div>
+ </div>
+ <div class="filler"></div>
+</div>
+<script>
+ test(() => {
+ scrollable.scrollTo(0, 5000);
+ assert_equals(scrollable.scrollTop, 5000, "Initially scrolls instantly");
+ scrollable.scrollTo(0, 0);
+ scrollable.className = "smooth";
+ scrollable.scrollTo(0, 5000);
+ assert_less_than(scrollable.scrollTop, 5000, "scroll-behavior:smooth should not scroll instantly");
+ scrollable.className = "";
+ scrollable.scrollTo(0, 0);
+ }, "scroll-behavior: smooth on DIV element");
+
+ test(() => {
+ window.scrollTo(0, 5000);
+ assert_equals(window.scrollY, 5000, "Initially scrolls instantly");
+ window.scrollTo(0, 0);
+ document.documentElement.className = "smooth";
+ assert_less_than(window.scrollY, 5000, "scroll-behavior:smooth should not scroll instantly");
+ document.documentElement.className = "";
+ window.scrollTo(0, 0);
+ }, "HTML element scroll-behavior should propagate to viewport");
+
+ test(() => {
+ window.scrollTo(0, 5000);
+ assert_equals(window.scrollY, 5000, "Initially scrolls instantly");
+ window.scrollTo(0, 0);
+ document.body.className = "smooth";
+ window.scrollTo(0, 5000);
+ assert_equals(window.scrollY, 5000, "scroll-behavior:smooth on BODY should scroll viewport instantly");
+ document.body.className = "";
+ window.scrollTo(0, 0);
+ }, "BODY element scroll-behavior should not propagate to viewport");
+
+ testContainer.style.display = "none";
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-behavior-subframe-root.html b/testing/web-platform/tests/css/cssom-view/scroll-behavior-subframe-root.html
new file mode 100644
index 0000000000..050817d416
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-behavior-subframe-root.html
@@ -0,0 +1,196 @@
+<!DOCTYPE html>
+<title>Testing scrollOptions' behavior for Element.scroll* and scroll-behavior on the root of a subframe</title>
+<meta name="timeout" content="long"/>
+<link rel="author" title="Frédéric Wang" href="mailto:fwang@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-box">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/scroll-behavior.js"></script>
+<div id="log">
+</div>
+<iframe id="iframeNode" width="400px" height="200px" srcdoc="<!DOCTYPE>
+<html>
+ <style>
+ body {
+ margin: 0;
+ }
+ .autoBehavior {
+ scroll-behavior: auto;
+ }
+ .smoothBehavior {
+ scroll-behavior: smooth;
+ }
+ </style>
+ <body>
+ <div style='width: 2000px; height: 1000px; background: linear-gradient(135deg, red, green);'>
+ <span style='display: inline-block; width: 500px; height: 250px;'></span><span id='elementToReveal' style='display: inline-block; vertical-align: -15px; width: 10px; height: 15px; background: black;'></span>
+ </div>
+ </body>
+</html>">
+</iframe>
+<script>
+ var iframeLoadTest = async_test("iframe loaded");
+ var scrollingElement, styledElement, elementToReveal;
+ var elementToRevealLeft = 500;
+ var elementToRevealTop = 250;
+ iframeNode.addEventListener("load", iframeLoadTest.step_func_done(() => {
+ scrollingElement = iframeNode.contentDocument.scrollingElement;
+ styledElement = iframeNode.contentDocument.documentElement;
+ elementToReveal = iframeNode.contentDocument.getElementById("elementToReveal");
+
+ ["scroll", "scrollTo", "scrollBy", "scrollIntoView"].forEach((scrollFunction) => {
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, null, elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Subframe with auto scroll-behavior ; ${scrollFunction}() with default behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "auto", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Subframe with auto scroll-behavior ; ${scrollFunction}() with auto behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "instant", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Subframe with auto scroll-behavior ; ${scrollFunction}() with instant behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "smooth", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Subframe with auto scroll-behavior ; ${scrollFunction}() with smooth behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, null, elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Subframe with smooth scroll-behavior ; ${scrollFunction}() with default behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "auto", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Subframe with smooth scroll-behavior ; ${scrollFunction}() with auto behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "instant", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Subframe with smooth scroll-behavior ; ${scrollFunction}() with instant behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, scrollFunction, "smooth", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingElement.scrollLeft, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingElement.scrollTop, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Subframe with smooth scroll-behavior ; ${scrollFunction}() with smooth behavior`);
+ });
+
+ [{scrollAttribute: "scrollLeft", scrollValue: elementToRevealLeft}, {scrollAttribute: "scrollTop", scrollValue: elementToRevealTop}].forEach((attributeTest) => {
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ var expectedValue = Number(attributeTest.scrollValue);
+ scrollingElement[attributeTest.scrollAttribute] = expectedValue;
+ assert_equals(scrollingElement[attributeTest.scrollAttribute], expectedValue, `Should set ${attributeTest.scrollAttribute} immediately`);
+ return new Promise((resolve) => { resolve(); });
+ }, `Subframe setting ${attributeTest.scrollAttribute} with auto scroll-behavior`);
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ var expectedValue = Number(attributeTest.scrollValue);
+ scrollingElement[attributeTest.scrollAttribute] = expectedValue;
+ assert_less_than(scrollingElement[attributeTest.scrollAttribute], expectedValue, `Should not set ${attributeTest.scrollAttribute} immediately`);
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement[attributeTest.scrollAttribute], expectedValue, `Final value of ${attributeTest.scrollAttribute}`);
+ });
+ }, `Subframe setting ${attributeTest.scrollAttribute} with smooth scroll-behavior`);
+ });
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, "scroll", "smooth", elementToRevealLeft, elementToRevealTop);
+ scrollNode(scrollingElement, "scroll", "smooth", elementToRevealLeft / 2, elementToRevealTop / 2);
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, elementToRevealLeft / 2, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, elementToRevealTop / 2, "Final value of scrollTop");
+ });
+ }, "Aborting an ongoing smooth scrolling on a subframe with another smooth scrolling");
+
+ promise_test(() => {
+ resetScroll(scrollingElement);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingElement.scrollLeft, 0);
+ assert_equals(scrollingElement.scrollTop, 0);
+ scrollNode(scrollingElement, "scroll", "smooth", elementToRevealLeft, elementToRevealTop);
+ scrollNode(scrollingElement, "scroll", "instant", 0, 0);
+ return waitForScrollEnd(scrollingElement).then(() => {
+ assert_equals(scrollingElement.scrollLeft, 0, "Final value of scrollLeft");
+ assert_equals(scrollingElement.scrollTop, 0, "Final value of scrollTop");
+ });
+ }, "Aborting an ongoing smooth scrolling on a subframe with an instant scrolling");
+ }));
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-behavior-subframe-window.html b/testing/web-platform/tests/css/cssom-view/scroll-behavior-subframe-window.html
new file mode 100644
index 0000000000..ef587fea49
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-behavior-subframe-window.html
@@ -0,0 +1,172 @@
+<!DOCTYPE html>
+<title>Testing scrollOptions' behavior for Element.scroll* and scroll-behavior on the root of a subframe</title>
+<meta name="timeout" content="long"/>
+<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
+<link rel="author" title="Frédéric Wang" href="mailto:fwang@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#propdef-scroll-behavior">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-box">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="support/scroll-behavior.js"></script>
+<div id="log">
+</div>
+<iframe id="iframeNode" width="400px" height="200px" srcdoc="<!DOCTYPE>
+<html>
+ <style>
+ body {
+ margin: 0;
+ }
+ .autoBehavior {
+ scroll-behavior: auto;
+ }
+ .smoothBehavior {
+ scroll-behavior: smooth;
+ }
+ </style>
+ <body>
+ <div style='width: 2000px; height: 1000px; background: linear-gradient(135deg, red, green);'>
+ <span style='display: inline-block; width: 500px; height: 250px;'></span><span id='elementToReveal' style='display: inline-block; vertical-align: -15px; width: 10px; height: 15px; background: black;'></span>
+ </div>
+ </body>
+</html>">
+</iframe>
+<script>
+ var iframeLoadTest = async_test("iframe loaded");
+ var scrollingWindow, styledElement, elementToReveal;
+ var elementToRevealLeft = 500;
+ var elementToRevealTop = 250;
+ iframeNode.addEventListener("load", iframeLoadTest.step_func_done(() => {
+ scrollingWindow = iframeNode.contentWindow;
+ styledElement = iframeNode.contentDocument.documentElement;
+ elementToReveal = iframeNode.contentDocument.getElementById("elementToReveal");
+
+ ["scroll", "scrollTo", "scrollBy"].forEach((scrollFunction) => {
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, null, elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with default behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "auto", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with auto behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "instant", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with instant behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "autoBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "smooth", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingWindow.scrollX, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingWindow.scrollY, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with auto scroll-behavior ; ${scrollFunction}() with smooth behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, null, elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingWindow.scrollX, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingWindow.scrollY, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with default behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "auto", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingWindow.scrollX, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingWindow.scrollY, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with auto behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "instant", elementToRevealLeft, elementToRevealTop);
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Should set scrollLeft immediately");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Should set scrollTop immediately");
+ return new Promise((resolve) => { resolve(); });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with instant behavior`);
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, scrollFunction, "smooth", elementToRevealLeft, elementToRevealTop);
+ assert_less_than(scrollingWindow.scrollX, elementToRevealLeft, "Should not set scrollLeft immediately");
+ assert_less_than(scrollingWindow.scrollY, elementToRevealTop, "Should not set scrollTop immediately");
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop, "Final value of scrollTop");
+ });
+ }, `Main frame with smooth scroll-behavior ; ${scrollFunction}() with smooth behavior`);
+ });
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, "scroll", "smooth", elementToRevealLeft, elementToRevealTop);
+ scrollWindow(scrollingWindow, "scroll", "smooth", elementToRevealLeft / 2, elementToRevealTop / 2);
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, elementToRevealLeft / 2, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, elementToRevealTop / 2, "Final value of scrollTop");
+ });
+ }, "Aborting an ongoing smooth scrolling on the main frame with another smooth scrolling");
+
+ promise_test(() => {
+ resetScrollForWindow(scrollingWindow);
+ setScrollBehavior(styledElement, "smoothBehavior");
+ assert_equals(scrollingWindow.scrollX, 0);
+ assert_equals(scrollingWindow.scrollY, 0);
+ scrollWindow(scrollingWindow, "scroll", "smooth", elementToRevealLeft, elementToRevealTop);
+ scrollWindow(scrollingWindow, "scroll", "instant", 0, 0);
+ return waitForScrollEnd(scrollingWindow.document.scrollingElement).then(() => {
+ assert_equals(scrollingWindow.scrollX, 0, "Final value of scrollLeft");
+ assert_equals(scrollingWindow.scrollY, 0, "Final value of scrollTop");
+ });
+ }, "Aborting an ongoing smooth scrolling on the main frame with an instant scrolling");
+
+ }));
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-no-layout-box.html b/testing/web-platform/tests/css/cssom-view/scroll-no-layout-box.html
new file mode 100644
index 0000000000..cc67ce76ed
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-no-layout-box.html
@@ -0,0 +1,21 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>cssom-view - Scrolling element with no layout box</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scroll">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#css-layout-box">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<div style="display: none">
+ <div id="elem"></div>
+</div>
+
+<script>
+test(() => {
+ const elem = document.getElementById('elem');
+ elem.scroll(1, 2);
+
+ assert_equals(elem.scrollTop, 0, "scrollTop should be unchanged");
+ assert_equals(elem.scrollLeft, 0, "scrollLeft should be unchanged");
+}, "scrolling an element with no CSS layout box should have no effect");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-overflow-clip-quirks-001.html b/testing/web-platform/tests/css/cssom-view/scroll-overflow-clip-quirks-001.html
new file mode 100644
index 0000000000..ae70083ac3
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-overflow-clip-quirks-001.html
@@ -0,0 +1,23 @@
+<!-- quirks -->
+<title>CSSOM scrollingElement reflects the propagated scroll to viewport correctly</title>
+<meta charset="utf-8">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#overflow-propagation">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-document-scrollingelement">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/5601">
+<style>
+ :root {
+ overflow: clip;
+ }
+ body {
+ overflow: scroll;
+ }
+</style>
+<script>
+test(function() {
+ assert_equals(document.scrollingElement, null);
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scroll-overflow-clip-quirks-002.html b/testing/web-platform/tests/css/cssom-view/scroll-overflow-clip-quirks-002.html
new file mode 100644
index 0000000000..8909125649
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scroll-overflow-clip-quirks-002.html
@@ -0,0 +1,20 @@
+<!-- quirks -->
+<title>CSSOM scrollingElement reflects the propagated scroll to viewport correctly</title>
+<meta charset="utf-8">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<link rel="help" href="https://drafts.csswg.org/css-overflow/#overflow-propagation">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-document-scrollingelement">
+<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/5601">
+<style>
+ body {
+ overflow: clip;
+ }
+</style>
+<script>
+test(function() {
+ assert_equals(document.scrollingElement, document.body);
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-fixed.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-fixed.html
new file mode 100644
index 0000000000..bab58dad68
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-fixed.html
@@ -0,0 +1,258 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>CSSOM View - scrollIntoView from position:fixed</title>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width,minimum-scale=1">
+ <link rel="author" title="David Bokan" href="mailto:bokan@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>
+ body {
+ width: 1000vw;
+ height: 1000vh;
+ /* stripes so we can see scroll offset more easily */
+ background: repeating-linear-gradient(
+ 45deg,
+ #A2CFD9,
+ #A2CFD9 100px,
+ #C3F3FF 100px,
+ #C3F3FF 200px
+ );
+ }
+
+ .fixedContainer {
+ position: fixed;
+ bottom: 10px;
+ left: 10px;
+ width: 150px;
+ height: 150px;
+ background-color: coral;
+ }
+
+ .fixedContainer.scrollable {
+ overflow: auto;
+ left: unset;
+ right: 10px;
+ }
+
+ button {
+ position: absolute;
+ margin: 5px;
+ }
+
+ .target {
+ position: absolute;
+ width: 10px;
+ height: 10px;
+ background-color: blue;
+ left: 50%;
+ top: 50%;
+ }
+
+ .scrollable .target {
+ left: 200%;
+ top: 200%;
+ }
+
+ iframe {
+ width: 96vw;
+ height: 300px;
+ position: absolute;
+ left: 2vw;
+ top: 100px;
+ }
+ </style>
+ <script>
+ </script>
+ </head>
+ <body>
+ <div style="width:90vw">
+ <p>
+ The orange boxes are position: fixed. Clicking ScrollIntoView in each box
+ will attempt to scroll into view the blue target element inside that fixed
+ container to block/inline: start (i.e. aligned with top left corner in RTL).
+ </p>
+ <p>
+ scrollIntoView from a position:fixed element must not scroll its
+ containing frame; however, it must scroll further ancestor scrollers as
+ the element isn't fixed in relation to them.
+ </p>
+ </div>
+ <iframe></iframe>
+ <div class="fixedContainer">
+ Box A
+ <button id="fixedContainerBtn">ScrollIntoView</button>
+ <div class="target"></div>
+ </div>
+ <div class="fixedContainer scrollable">
+ Box C
+ <button id="scrollableFixedContainerBtn">ScrollIntoView</button>
+ <div class="target"></div>
+ </div>
+ <script>
+ if (typeof setup != 'undefined') {
+ setup({ explicit_done: true });
+ window.addEventListener("load", runTests);
+ }
+
+ function scrollIntoView(evt) {
+ const container = evt.target.closest('.fixedContainer');
+ const target = container.querySelector('.target');
+ target.scrollIntoView({block: 'start', inline: 'start'});
+ }
+
+ document.querySelectorAll('button').forEach((btn) => {
+ btn.addEventListener('click', scrollIntoView);
+ });
+
+ const iframe = document.querySelector('iframe');
+ iframe.onload = () => {
+ frames[0].document.querySelectorAll('button').forEach((btn) => {
+ btn.addEventListener('click', scrollIntoView);
+ });
+ }
+ iframe.srcdoc = `
+ <!DOCTYPE html>
+ <style>
+ body {
+ height: 200vh;
+ width: 200vw;
+ /* stripes so we can see scroll offset more easily */
+ background: repeating-linear-gradient(
+ 45deg,
+ #C3A2D9,
+ #C3A2D9 50px,
+ #E5C0FF 50px,
+ #E5C0FF 100px
+ );
+ }
+ .fixedContainer {
+ position: fixed;
+ bottom: 10px;
+ left: 10px;
+ width: 150px;
+ height: 150px;
+ background-color: coral;
+ }
+
+ .fixedContainer.scrollable {
+ overflow: auto;
+ left: unset;
+ right: 10px;
+ }
+
+ button {
+ position: absolute;
+ margin: 5px;
+ }
+
+ .target {
+ position: absolute;
+ width: 10px;
+ height: 10px;
+ background-color: blue;
+ left: 50%;
+ top: 50%;
+ }
+
+ .scrollable .target {
+ left: 200%;
+ top: 200%;
+ }
+ </style>
+ IFRAME
+ <div class="fixedContainer">
+ Box B
+ <button id="fixedContainerBtn">ScrollIntoView</button>
+ <div class="target"></div>
+ </div>
+ <div class="fixedContainer scrollable">
+ Box D
+ <button id="scrollableFixedContainerBtn">ScrollIntoView</button>
+ <div class="target"></div>
+ </div>
+ `;
+
+ function reset() {
+ [document, frames[0].document].forEach((doc) => {
+ doc.scrollingElement.scrollLeft = 0;
+ doc.scrollingElement.scrollTop = 0;
+ doc.querySelectorAll('.fixedContainer').forEach((e) => {
+ e.scrollLeft = 0;
+ e.scrollTop = 0;
+ });
+ });
+ }
+
+ function runTests() {
+ // Test scrollIntoView from a plain, unscrollable position:fixed element.
+ // Nothing should scroll.
+ test(() => {
+ reset()
+ const container = document.querySelector('.fixedContainer:not(.scrollable)');
+ const target = container.querySelector('.target');
+ target.scrollIntoView({block: 'start', inline: 'start'});
+ assert_equals(window.scrollX, 0, 'must not scroll window [scrollX]');
+ assert_equals(window.scrollY, 0, 'must not scroll window [scrollY]');
+ }, `[Box A] scrollIntoView from unscrollable position:fixed`);
+
+ // Same as above but from inside an iframe. Since the container is fixed
+ // only to the iframe, we should scroll the outer window.
+ test(() => {
+ reset()
+ const container = frames[0].document.querySelector('.fixedContainer:not(.scrollable)');
+ const target = container.querySelector('.target');
+ target.scrollIntoView({block: 'start', inline: 'start'});
+
+ // Large approx to account for differences like scrollbars
+ assert_approx_equals(window.scrollX, 100, 20, 'must scroll outer window [scrollX]');
+ assert_approx_equals(window.scrollY, 300, 20, 'must scroll outer window [scrollY]');
+ assert_equals(frames[0].window.scrollX, 0, 'must not scroll iframe [scrollX]');
+ assert_equals(frames[0].window.scrollY, 0, 'must not scroll iframe [scrollY]');
+ }, `[Box B] scrollIntoView from unscrollable position:fixed in iframe`);
+
+ // Test scrollIntoView from a scroller that's position fixed. The
+ // scroller should be scrolled but shouldn't bubble the scroll as the
+ // window scrolling won't change the target's position.
+ test(() => {
+ reset()
+ const container = document.querySelector('.fixedContainer.scrollable');
+ const target = container.querySelector('.target');
+ target.scrollIntoView({block: 'start', inline: 'start'});
+ // Large approx to account for differences like scrollbars
+ assert_equals(window.scrollX, 0, 'must not scroll window [scrollX]');
+ assert_equals(window.scrollY, 0, 'must not scroll window [scrollY]');
+ assert_approx_equals(container.scrollLeft, 145, 20,
+ 'scrollIntoView in container [scrollLeft]');
+ assert_approx_equals(container.scrollTop, 145, 20,
+ 'scrollIntoView in container [scrollTop]');
+ }, `[Box C] scrollIntoView from scrollable position:fixed`);
+
+ // Same as above but from inside an iframe. In this case, the scroller
+ // should bubble the scroll but skip its own frame (as it's fixed with
+ // respect to its own frame's scroll offset) and bubble to the outer
+ // window.
+ test(() => {
+ reset()
+ const container = frames[0].document.querySelector('.fixedContainer.scrollable');
+ const target = container.querySelector('.target');
+ target.scrollIntoView({block: 'start', inline: 'start'});
+ // Large approx to account for differences like scrollbars
+ assert_approx_equals(window.scrollX, 740, 20, 'must scroll outer window [scrollX]');
+ assert_approx_equals(window.scrollY, 360, 20, 'must scroll outer window [scrollY]');
+ assert_approx_equals(container.scrollLeft, 145, 20,
+ 'scrollIntoView in container [scrollLeft]');
+ assert_approx_equals(container.scrollTop, 145, 20,
+ 'scrollIntoView in container [scrollTop]');
+ assert_equals(frames[0].window.scrollX, 0, 'must not scroll iframe [scrollX]');
+ assert_equals(frames[0].window.scrollY, 0, 'must not scroll iframe [scrollY]');
+ }, `[Box D] scrollIntoView from scrollable position:fixed in iframe`);
+
+ done();
+ }
+
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-partially-visible.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-partially-visible.html
new file mode 100644
index 0000000000..1e8bc50071
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-partially-visible.html
@@ -0,0 +1,47 @@
+<!doctype html>
+<title>CSSOM View - scrollIntoView scrolls partially-visible element in both inline and block directions.</title>
+<meta charset="utf-8">
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview">
+<link rel="help" href="https://bugs.webkit.org/show_bug.cgi?id=203497">
+<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=916631">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ #scroller {
+ width: 200px;
+ height: 200px;
+ padding-top: 200px;
+ padding-left: 200px;
+ background: purple;
+ overflow: hidden;
+ }
+ #child {
+ width: 400px;
+ height: 400px;
+ background: green;
+ }
+</style>
+<div id="scroller">
+ <div id="child"></div>
+</div>
+<script>
+ test(function() {
+ let scroller = document.getElementById("scroller");
+ let child = document.getElementById("child");
+
+ scroller.scrollTop = 0;
+ scroller.scrollLeft = 0;
+
+ assert_equals(scroller.scrollTop, 0, "Precondition");
+ assert_equals(scroller.scrollLeft, 0, "Precondition");
+ assert_not_equals(scroller.scrollTopMax, 0, "Precondition")
+ assert_not_equals(scroller.scrollLeftMax, 0, "Precondition")
+
+ child.scrollIntoView();
+
+ assert_equals(scroller.scrollTop, 200, "Should have scrolled in the block direction");
+ assert_equals(scroller.scrollLeft, 200, "Should have scrolled in the inline direction");
+ }, "scrollIntoView scrolls partially-visible child in both axes");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-tb-writing-mode-and-rtl-direction.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-tb-writing-mode-and-rtl-direction.html
new file mode 100644
index 0000000000..3ef9d62a36
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-tb-writing-mode-and-rtl-direction.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView considers horizontal-tb and rtl direction</title>
+<meta charset="utf-8">
+<link rel="author" title="Cathie Chen" href="mailto:cathiechen@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scroll-an-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+.box {
+ float: left;
+ width: 200px;
+ height: 200px;
+}
+#scroller {
+ direction: rtl;
+ overflow: scroll;
+ width: 300px;
+ height: 300px;
+}
+#container{
+ width: 600px;
+ height: 600px;
+}
+#target {
+ background-color: #ff0;
+}
+</style>
+<body>
+<div id="scroller">
+ <div id="container">
+ <!-- ROW-1 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-2 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box" id="target"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-3 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+ </div>
+</div>
+
+<script>
+// In horizontal-tb mode and rtl direction, X corresponds to the inline axis
+// and is oriented leftward. Y corresponds to the block axis and is oriented
+// downward. So the beginning edges are the top and right edges and the ending
+// edges are the bottom and left edges.
+
+// According to the spec, x is min(0, max(x, element padding edge width - element scrolling area width)).
+// So x is nonpositive and decreases leftward.
+
+// This assumes that the horizontal scrollbar is on the bottom side
+// and the vertical scrollbar is on the left side.
+
+var target = document.getElementById("target");
+var scroller = document.getElementById("scroller");
+var scrollbar_width = scroller.offsetWidth - scroller.clientWidth;
+
+var scroller_width = scroller.offsetWidth;
+var scroller_height = scroller.offsetHeight;
+var box_width = target.offsetWidth;
+var box_height = target.offsetHeight;
+
+var expectedX = {
+ inlineStart: -box_width,
+ inlineCenter: -((3*box_width - scroller_width)/2) - scrollbar_width/2,
+ inlineEnd: -(2*box_width - scroller_width) - scrollbar_width,
+};
+
+var expectedY = {
+ blockStart: box_height,
+ blockCenter: (3*box_height - scroller_height)/2 + scrollbar_width/2,
+ blockEnd: 2*box_height - scroller_height + scrollbar_width,
+};
+
+[
+ [{block: "start", inline: "start"}, expectedX.inlineStart, expectedY.blockStart],
+ [{block: "start", inline: "center"}, expectedX.inlineCenter, expectedY.blockStart],
+ [{block: "start", inline: "end"}, expectedX.inlineEnd, expectedY.blockStart],
+ [{block: "center", inline: "start"}, expectedX.inlineStart, expectedY.blockCenter],
+ [{block: "center", inline: "center"}, expectedX.inlineCenter, expectedY.blockCenter],
+ [{block: "center", inline: "end"}, expectedX.inlineEnd, expectedY.blockCenter],
+ [{block: "end", inline: "start"}, expectedX.inlineStart, expectedY.blockEnd],
+ [{block: "end", inline: "center"}, expectedX.inlineCenter, expectedY.blockEnd],
+ [{block: "end", inline: "end"}, expectedX.inlineEnd, expectedY.blockEnd],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ scroller.scrollTo(0, 0);
+ target.scrollIntoView(input);
+ assert_approx_equals(scroller.scrollLeft, expectedX, 0.5, "scrollX");
+ assert_approx_equals(scroller.scrollTop, expectedY, 0.5, "scrollY");
+ }, `scrollIntoView(${JSON.stringify(input)})`);
+})
+
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-tb-writing-mode.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-tb-writing-mode.html
new file mode 100644
index 0000000000..49d5f5d865
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-horizontal-tb-writing-mode.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView considers horizontal-tb writing mode</title>
+<meta charset="utf-8">
+<link rel="author" title="Cathie Chen" href="mailto:cathiechen@igalia.com">
+<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>
+.box {
+ float: left;
+ width: 200px;
+ height: 200px;
+}
+#scroller {
+ overflow: scroll;
+ width: 300px;
+ height: 300px;
+}
+#container{
+ width: 600px;
+ height: 600px;
+}
+#target {
+ background-color: #ff0;
+}
+</style>
+<body>
+<div id="scroller">
+ <div id="container">
+ <!-- ROW-1 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-2 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box" id="target"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-3 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+ </div>
+</div>
+
+<script>
+// In horizontal-tb mode, X corresponds to the inline axis and is oriented
+// rightward. Y corresponds to the block axis and is oriented downward.
+// So the beginning edges are the top and left edges and the ending
+// edges are the bottom and right edges.
+
+// This assumes that the horizontal scrollbar is on the bottom side and
+// the vertical scrollbar is on the right side.
+
+var target = document.getElementById("target");
+var scroller = document.getElementById("scroller");
+var scrollbar_width = scroller.offsetWidth - scroller.clientWidth;
+
+var scroller_width = scroller.offsetWidth;
+var scroller_height = scroller.offsetHeight;
+var box_width = target.offsetWidth;
+var box_height = target.offsetHeight;
+
+var expectedX = {
+ inlineStart: box_width,
+ inlineCenter: (3*box_width - scroller_width)/2 + scrollbar_width/2,
+ inlineEnd: 2*box_width - scroller_width + scrollbar_width,
+ };
+
+var expectedY = {
+ blockStart: box_height,
+ blockCenter: (3*box_height - scroller_height)/2 + scrollbar_width/2,
+ blockEnd: 2*box_height - scroller_height + scrollbar_width,
+};
+
+[
+ [{block: "start", inline: "start"}, expectedX.inlineStart, expectedY.blockStart],
+ [{block: "start", inline: "center"}, expectedX.inlineCenter, expectedY.blockStart],
+ [{block: "start", inline: "end"}, expectedX.inlineEnd, expectedY.blockStart],
+ [{block: "center", inline: "start"}, expectedX.inlineStart, expectedY.blockCenter],
+ [{block: "center", inline: "center"}, expectedX.inlineCenter, expectedY.blockCenter],
+ [{block: "center", inline: "end"}, expectedX.inlineEnd, expectedY.blockCenter],
+ [{block: "end", inline: "start"}, expectedX.inlineStart, expectedY.blockEnd],
+ [{block: "end", inline: "center"}, expectedX.inlineCenter, expectedY.blockEnd],
+ [{block: "end", inline: "end"}, expectedX.inlineEnd, expectedY.blockEnd],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ scroller.scrollTo(0, 0);
+ target.scrollIntoView(input);
+ assert_approx_equals(scroller.scrollLeft, expectedX, 0.5, "scrollX");
+ assert_approx_equals(scroller.scrollTop, expectedY, 0.5, "scrollY");
+ }, `scrollIntoView(${JSON.stringify(input)})`);
+})
+
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-inline-image.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-inline-image.html
new file mode 100644
index 0000000000..1bdc75a27a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-inline-image.html
@@ -0,0 +1,26 @@
+<!doctype html>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<title>scrollIntoView uses the element's bounding rect rather than line box</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scroll-an-element-into-view">
+<style>
+img {
+ height: 100vh;
+ width: 100px;
+}
+body {
+ margin-bottom: 100vh;
+}
+</style>
+<body>
+<a id="scrollto">To be scrolled into view</a><img src="/images/blue.png">
+</body>
+<script>
+
+test(t => {
+ window.scrollTo(0, 0);
+ const target = document.getElementById("scrollto");
+ target.scrollIntoView();
+ assert_approx_equals(window.scrollY, target.offsetTop, 1);
+}, "Scrolling an inline element with a large line height uses the bounding rect");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-scrollMargin.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-scrollMargin.html
new file mode 100644
index 0000000000..930702aa87
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-scrollMargin.html
@@ -0,0 +1,78 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView considers scroll-margin</title>
+<meta charset="utf-8">
+<link rel="author" title="Sandra Sun" href="mailto:sunyunjia@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview">
+<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-margin">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+#scroller {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+}
+#content {
+ width: 500px;
+ height: 500px;
+}
+#target {
+ position: relative;
+ left: 200px;
+ top: 200px;
+ width: 100px;
+ height: 100px;
+ scroll-margin-top: 4px;
+ scroll-margin-right: 8px;
+ scroll-margin-bottom: 12px;
+ scroll-margin-left: 16px;
+ background-color: lightgreen;
+}
+</style>
+
+<div id="scroller">
+ <div id="content">
+ <div id="target"></div>
+ </div>
+</div>
+<div id="log"></div>
+
+<script>
+var target = document.getElementById("target");
+var scroller = document.getElementById("scroller");
+
+var expectedXLeft = 200 - 16;
+var expectedXRight = 200 - scroller.clientWidth + target.clientWidth + 8;
+var expectedXCenter = 200 - (scroller.clientWidth / 2) +
+ (target.clientWidth + 8 - 16) / 2;
+
+var expectedYTop = 200 - 4;
+var expectedYBottom = 200 - scroller.clientHeight + target.clientHeight + 12;
+var expectedYCenter = 200 - (scroller.clientHeight / 2) +
+ (target.clientHeight + 12 - 4) / 2;
+
+// This formats dict as a string suitable as test name.
+// format_value() is provided by testharness.js,
+// which also preserves sign for -0.
+function format_dict(dict) {
+ const props = [];
+ for (let prop in dict) {
+ props.push(`${prop}: ${format_value(dict[prop])}`);
+ }
+ return `{${props.join(", ")}}`;
+}
+
+[
+ [{block: "center", inline: "center"}, expectedXCenter, expectedYCenter],
+ [{block: "start", inline: "start"}, expectedXLeft, expectedYTop],
+ [{block: "end", inline: "end"}, expectedXRight, expectedYBottom],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ scroller.scrollTo(0, 0);
+ target.scrollIntoView(input);
+ assert_approx_equals(scroller.scrollLeft, expectedX, 0.5, "scrollX");
+ assert_approx_equals(scroller.scrollTop, expectedY, 0.5, "scrollY");
+ }, `scrollIntoView(${format_dict(input)})`);
+})
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-scrollPadding.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-scrollPadding.html
new file mode 100644
index 0000000000..5fdedf753d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-scrollPadding.html
@@ -0,0 +1,77 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView considers scroll-padding</title>
+<meta charset="utf-8">
+<link rel="author" title="Sandra Sun" href="mailto:sunyunjia@chromium.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview">
+<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+#scroller {
+ width: 200px;
+ height: 200px;
+ overflow: scroll;
+ scroll-padding-top: 4px;
+ scroll-padding-right: 8px;
+ scroll-padding-bottom: 12px;
+ scroll-padding-left: 16px;
+}
+#content {
+ width: 500px;
+ height: 500px;
+}
+#target {
+ position: relative;
+ left: 200px;
+ top: 200px;
+ width: 100px;
+ height: 100px;
+ background-color: lightgreen;
+}
+</style>
+
+<div id="scroller">
+ <div id="content">
+ <div id="target"></div>
+ </div>
+</div>
+<div id="log"></div>
+
+<script>
+var target = document.getElementById("target");
+var scroller = document.getElementById("scroller");
+var expectedXLeft = 200 - 16;
+var expectedXRight = 200 - scroller.clientWidth + 8 + target.clientWidth;
+var expectedXCenter = 200 - (16 + scroller.clientWidth - 8) / 2 +
+ target.clientWidth / 2;
+
+var expectedYTop = 200 - 4;
+var expectedYBottom = 200 - scroller.clientHeight + 12 + target.clientHeight;
+var expectedYCenter = 200 - (4 + scroller.clientHeight - 12) / 2 +
+ target.clientHeight / 2;
+
+// This formats dict as a string suitable as test name.
+// format_value() is provided by testharness.js,
+// which also preserves sign for -0.
+function format_dict(dict) {
+ const props = [];
+ for (let prop in dict) {
+ props.push(`${prop}: ${format_value(dict[prop])}`);
+ }
+ return `{${props.join(", ")}}`;
+}
+
+[
+ [{block: "center", inline: "center"}, expectedXCenter, expectedYCenter],
+ [{block: "start", inline: "start"}, expectedXLeft, expectedYTop],
+ [{block: "end", inline: "end"}, expectedXRight, expectedYBottom],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ scroller.scrollTo(0, 0);
+ target.scrollIntoView(input);
+ assert_approx_equals(scroller.scrollLeft, expectedX, 0.5, "scrollX");
+ assert_approx_equals(scroller.scrollTop, expectedY, 0.5, "scrollY");
+ }, `scrollIntoView(${format_dict(input)})`);
+})
+</script> \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-shadow.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-shadow.html
new file mode 100644
index 0000000000..3b19e1ef5a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-shadow.html
@@ -0,0 +1,34 @@
+<!DOCTYPE HTML>
+<meta name="viewport" content="user-scalable=no">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<title>Check End Position of scrollIntoView of shadow elements</title>
+<div id="container">
+ <div id="space1" style="height: 2000px; width: 2000px;background-color: yellow">
+ </div>
+ <div id="shadow"></div>
+ <div id="space2" style="height: 2000px; width: 2000px;background-color: blue">
+ </div>
+</div>
+<script>
+add_completion_callback(() => document.getElementById("container").remove());
+
+test(t => {
+ var shadow = document.getElementById("shadow");
+ var shadowRoot = shadow.attachShadow({ mode: "open" });
+ var shadowDiv = document.createElement("div");
+ shadowDiv.style.height = "200px";
+ shadowDiv.style.width = "200px";
+ shadowDiv.style.backgroundColor = "green";
+ shadowRoot.appendChild(shadowDiv);
+
+ window.scrollTo(0, 0);
+ var expected_x = shadowDiv.offsetLeft;
+ var expected_y = shadowDiv.offsetTop;
+ assert_not_equals(window.scrollX, expected_x);
+ assert_not_equals(window.scrollY, expected_y);
+ shadowDiv.scrollIntoView({block: "start", inline: "start"});
+ assert_approx_equals(window.scrollX, expected_x, 1);
+ assert_approx_equals(window.scrollY, expected_y, 1);
+}, "scrollIntoView should behave correctly if applies to shadow dom elements");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-lr-writing-mode-and-rtl-direction.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-lr-writing-mode-and-rtl-direction.html
new file mode 100644
index 0000000000..8d435407f6
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-lr-writing-mode-and-rtl-direction.html
@@ -0,0 +1,106 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView considers sideways-lr writing mode and rtl direction</title>
+<meta charset="utf-8">
+<link rel="author" title="Cathie Chen" href="mailto:cathiechen@igalia.com">
+<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>
+.box {
+ float: left;
+ width: 200px;
+ height: 200px;
+}
+#scroller {
+ writing-mode: sideways-lr;
+ direction: rtl;
+ overflow: scroll;
+ width: 300px;
+ height: 300px;
+}
+#container{
+ width: 600px;
+ height: 600px;
+}
+#target {
+ background-color: #ff0;
+}
+</style>
+<body>
+<div id="scroller">
+ <div id="container">
+ <!-- ROW-1 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-2 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box" id="target"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-3 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+ </div>
+</div>
+
+<script>
+// In sideways-lr mode and rtl direction, X corresponds to the block axis and is oriented rightward.
+// Y corresponds to the inline axis and is oriented downward.
+// So the beginning edges are the top and left edges and the ending edges are the bottom and right edges.
+
+// This assumes that the horizontal scrollbar is on the bottom side and the vertical scrollbar is on the right side.
+
+var target = document.getElementById("target");
+var scroller = document.getElementById("scroller");
+var scrollbar_width = scroller.offsetWidth - scroller.clientWidth;
+
+var scroller_width = scroller.offsetWidth;
+var scroller_height = scroller.offsetHeight;
+var box_width = target.offsetWidth;
+var box_height = target.offsetHeight;
+
+var expectedX = {
+ blockStart: box_width,
+ blockCenter: (3*box_width - scroller_width)/2 + scrollbar_width/2,
+ blockEnd: 2*box_width - scroller_width + scrollbar_width,
+};
+
+var expectedY = {
+ inlineStart: box_height,
+ inlineCenter: (3*box_height - scroller_height)/2 + scrollbar_width/2,
+ inlineEnd: 2*box_height - scroller_height + scrollbar_width,
+};
+
+[
+ [{block: "start", inline: "start"}, expectedX.blockStart, expectedY.inlineStart],
+ [{block: "start", inline: "center"}, expectedX.blockStart, expectedY.inlineCenter],
+ [{block: "start", inline: "end"}, expectedX.blockStart, expectedY.inlineEnd],
+ [{block: "center", inline: "start"}, expectedX.blockCenter, expectedY.inlineStart],
+ [{block: "center", inline: "center"}, expectedX.blockCenter, expectedY.inlineCenter],
+ [{block: "center", inline: "end"}, expectedX.blockCenter, expectedY.inlineEnd],
+ [{block: "end", inline: "start"}, expectedX.blockEnd, expectedY.inlineStart],
+ [{block: "end", inline: "center"}, expectedX.blockEnd, expectedY.inlineCenter],
+ [{block: "end", inline: "end"}, expectedX.blockEnd, expectedY.inlineEnd],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ scroller.scrollTo(0, 0);
+ target.scrollIntoView(input);
+ assert_approx_equals(scroller.scrollLeft, expectedX, 0.5, "scrollX");
+ assert_approx_equals(scroller.scrollTop, expectedY, 0.5, "scrollY");
+ }, `scrollIntoView(${JSON.stringify(input)})`);
+})
+
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-lr-writing-mode.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-lr-writing-mode.html
new file mode 100644
index 0000000000..0659dec8c1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-lr-writing-mode.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView considers sideways-rl writing mode</title>
+<meta charset="utf-8">
+<link rel="author" title="Cathie Chen" href="mailto:cathiechen@igalia.com">
+<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>
+.box {
+ float: left;
+ width: 200px;
+ height: 200px;
+}
+#scroller {
+ writing-mode: sideways-lr;
+ overflow: scroll;
+ width: 300px;
+ height: 300px;
+}
+#container{
+ width: 600px;
+ height: 600px;
+}
+#target {
+ background-color: #ff0;
+}
+</style>
+<body>
+<div id="scroller">
+ <div id="container">
+ <!-- ROW-1 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-2 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box" id="target"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-3 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+ </div>
+</div>
+
+<script>
+// In sideways-lr mode, X corresponds to the block axis and is oriented rightward.
+// Y corresponds to the inline axis and is oriented upward.
+// So the beginning edges are the bottom and left edges and the ending edges are the top and right edges.
+
+// According to the spec, y be min(0, max(y, element padding edge height - element scrolling area height)).
+// So y is nonpositive and decreases upward.
+
+// This assumes that the horizontal scrollbar is on the bottom side and the vertical scrollbar is on the right side.
+
+var target = document.getElementById("target");
+var scroller = document.getElementById("scroller");
+var scrollbar_width = scroller.offsetWidth - scroller.clientWidth;
+
+var scroller_width = scroller.offsetWidth;
+var scroller_height = scroller.offsetHeight;
+var box_width = target.offsetWidth;
+var box_height = target.offsetHeight;
+
+var expectedX = {
+ blockStart: box_width,
+ blockCenter: (3*box_width - scroller_width)/2 + scrollbar_width/2,
+ blockEnd: 2*box_width - scroller_width + scrollbar_width,
+};
+
+var expectedY = {
+ inlineStart: -box_height,
+ inlineCenter: -((3*box_height - scroller_height)/2) - scrollbar_width/2,
+ inlineEnd: -(2*box_height - scroller_height) - scrollbar_width,
+};
+
+[
+ [{block: "start", inline: "start"}, expectedX.blockStart, expectedY.inlineStart],
+ [{block: "start", inline: "center"}, expectedX.blockStart, expectedY.inlineCenter],
+ [{block: "start", inline: "end"}, expectedX.blockStart, expectedY.inlineEnd],
+ [{block: "center", inline: "start"}, expectedX.blockCenter, expectedY.inlineStart],
+ [{block: "center", inline: "center"}, expectedX.blockCenter, expectedY.inlineCenter],
+ [{block: "center", inline: "end"}, expectedX.blockCenter, expectedY.inlineEnd],
+ [{block: "end", inline: "start"}, expectedX.blockEnd, expectedY.inlineStart],
+ [{block: "end", inline: "center"}, expectedX.blockEnd, expectedY.inlineCenter],
+ [{block: "end", inline: "end"}, expectedX.blockEnd, expectedY.inlineEnd],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ scroller.scrollTo(0, 0);
+ target.scrollIntoView(input);
+ assert_approx_equals(scroller.scrollLeft, expectedX, 0.5, "scrollX");
+ assert_approx_equals(scroller.scrollTop, expectedY, 0.5, "scrollY");
+ }, `scrollIntoView(${JSON.stringify(input)})`);
+})
+
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-rl-writing-mode-and-rtl-direction.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-rl-writing-mode-and-rtl-direction.html
new file mode 100644
index 0000000000..82e43eb485
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-rl-writing-mode-and-rtl-direction.html
@@ -0,0 +1,111 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView considers sideways-rl writing mode and rtl direction</title>
+<meta charset="utf-8">
+<link rel="author" title="Cathie Chen" href="mailto:cathiechen@igalia.com">
+<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>
+.box {
+ float: left;
+ width: 200px;
+ height: 200px;
+}
+#scroller {
+ writing-mode: sideways-rl;
+ direction: rtl;
+ overflow: scroll;
+ width: 300px;
+ height: 300px;
+}
+#container{
+ width: 600px;
+ height: 600px;
+}
+#target {
+ background-color: #ff0;
+}
+</style>
+<body>
+<div id="scroller">
+ <div id="container">
+ <!-- ROW-1 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-2 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box" id="target"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-3 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+ </div>
+</div>
+
+<script>
+// In sideways-rl mode and rtl direction, X corresponds to the block axis and is oriented leftward.
+// Y corresponds to the inline axis and is oriented upward.
+// So the beginning edges are the bottom and right edges and the ending edges are the top and left edges.
+
+// According to the spec, x be min(0, max(x, element padding edge width - element scrolling area width)).
+// So x is nonpositive and decreases leftward.
+// According to the spec, y be min(0, max(y, element padding edge height - element scrolling area height)).
+// So y is nonpositive and decreases upward.
+
+// This assumes that the horizontal scrollbar is on the bottom side and the vertical scrollbar is on the left side.
+
+var target = document.getElementById("target");
+var scroller = document.getElementById("scroller");
+var scrollbar_width = scroller.offsetWidth - scroller.clientWidth;
+
+var scroller_width = scroller.offsetWidth;
+var scroller_height = scroller.offsetHeight;
+var box_width = target.offsetWidth;
+var box_height = target.offsetHeight;
+
+var expectedX = {
+ blockStart: -box_width,
+ blockCenter: -((3*box_width - scroller_width)/2) - scrollbar_width/2,
+ blockEnd: -(2*box_width - scroller_width) - scrollbar_width,
+};
+
+var expectedY = {
+ inlineStart: -box_height,
+ inlineCenter: -((3*box_height - scroller_height)/2) - scrollbar_width/2,
+ inlineEnd: -(2*box_height - scroller_height) - scrollbar_width,
+};
+
+[
+ [{block: "start", inline: "start"}, expectedX.blockStart, expectedY.inlineStart],
+ [{block: "start", inline: "center"}, expectedX.blockStart, expectedY.inlineCenter],
+ [{block: "start", inline: "end"}, expectedX.blockStart, expectedY.inlineEnd],
+ [{block: "center", inline: "start"}, expectedX.blockCenter, expectedY.inlineStart],
+ [{block: "center", inline: "center"}, expectedX.blockCenter, expectedY.inlineCenter],
+ [{block: "center", inline: "end"}, expectedX.blockCenter, expectedY.inlineEnd],
+ [{block: "end", inline: "start"}, expectedX.blockEnd, expectedY.inlineStart],
+ [{block: "end", inline: "center"}, expectedX.blockEnd, expectedY.inlineCenter],
+ [{block: "end", inline: "end"}, expectedX.blockEnd, expectedY.inlineEnd],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ scroller.scrollTo(0, 0);
+ target.scrollIntoView(input);
+ assert_approx_equals(scroller.scrollLeft, expectedX, 0.5, "scrollX");
+ assert_approx_equals(scroller.scrollTop, expectedY, 0.5, "scrollY");
+ }, `scrollIntoView(${JSON.stringify(input)})`);
+})
+
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-rl-writing-mode.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-rl-writing-mode.html
new file mode 100644
index 0000000000..9d7fda63e6
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-sideways-rl-writing-mode.html
@@ -0,0 +1,108 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView considers sideways-rl writing mode</title>
+<meta charset="utf-8">
+<link rel="author" title="Cathie Chen" href="mailto:cathiechen@igalia.com">
+<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>
+.box {
+ float: left;
+ width: 200px;
+ height: 200px;
+}
+#scroller {
+ writing-mode: sideways-rl;
+ overflow: scroll;
+ width: 300px;
+ height: 300px;
+}
+#container{
+ width: 600px;
+ height: 600px;
+}
+#target {
+ background-color: #ff0;
+}
+</style>
+<body>
+<div id="scroller">
+ <div id="container">
+ <!-- ROW-1 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-2 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box" id="target"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-3 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+ </div>
+</div>
+
+<script>
+// In sideways-rl mode, X corresponds to the block axis and is oriented leftward.
+// Y corresponds to the inline axis and is oriented downward.
+// So the beginning edges are the top and right edges and the ending edges are the bottom and left edges.
+
+// According to the spec, x be min(0, max(x, element padding edge width - element scrolling area width)).
+// So x is nonpositive and decreases leftward.
+
+// This assumes that the horizontal scrollbar is on the bottom side and the vertical scrollbar is on the left side.
+
+var target = document.getElementById("target");
+var scroller = document.getElementById("scroller");
+var scrollbar_width = scroller.offsetWidth - scroller.clientWidth;
+
+var scroller_width = scroller.offsetWidth;
+var scroller_height = scroller.offsetHeight;
+var box_width = target.offsetWidth;
+var box_height = target.offsetHeight;
+
+var expectedX = {
+ blockStart: -box_width,
+ blockCenter: -((3*box_width - scroller_width)/2) - scrollbar_width/2,
+ blockEnd: -(2*box_width - scroller_width) - scrollbar_width,
+};
+
+var expectedY = {
+ inlineStart: box_height,
+ inlineCenter: ((3*box_height - scroller_height)/2) + (scrollbar_width/2),
+ inlineEnd: ((2*box_height) - scroller_height) + scrollbar_width,
+};
+
+[
+ [{block: "start", inline: "start"}, expectedX.blockStart, expectedY.inlineStart],
+ [{block: "start", inline: "center"}, expectedX.blockStart, expectedY.inlineCenter],
+ [{block: "start", inline: "end"}, expectedX.blockStart, expectedY.inlineEnd],
+ [{block: "center", inline: "start"}, expectedX.blockCenter, expectedY.inlineStart],
+ [{block: "center", inline: "center"}, expectedX.blockCenter, expectedY.inlineCenter],
+ [{block: "center", inline: "end"}, expectedX.blockCenter, expectedY.inlineEnd],
+ [{block: "end", inline: "start"}, expectedX.blockEnd, expectedY.inlineStart],
+ [{block: "end", inline: "center"}, expectedX.blockEnd, expectedY.inlineCenter],
+ [{block: "end", inline: "end"}, expectedX.blockEnd, expectedY.inlineEnd],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ scroller.scrollTo(0, 0);
+ target.scrollIntoView(input);
+ assert_approx_equals(scroller.scrollLeft, expectedX, 0.5, "scrollX");
+ assert_approx_equals(scroller.scrollTop, expectedY, 0.5, "scrollY");
+ }, `scrollIntoView(${JSON.stringify(input)})`);
+})
+
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-smooth.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-smooth.html
new file mode 100644
index 0000000000..70343cb425
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-smooth.html
@@ -0,0 +1,101 @@
+<!DOCTYPE HTML>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<title>Check End Position of smooth scrollIntoView</title>
+<div id="container" style="height: 2500px; width: 2500px;">
+ <div id="content" style="height: 500px; width: 500px;margin-left: 1000px; margin-right: 1000px; margin-top: 1000px;margin-bottom: 1000px;background-color: red">
+ </div>
+ <div id="shadow"></div>
+</div>
+<script>
+var content_height = 500;
+var content_width = 500;
+var window_height = document.documentElement.clientHeight;
+var window_width = document.documentElement.clientWidth;
+var content = document.getElementById("content");
+add_completion_callback(() => document.getElementById("container").remove());
+
+function waitForScrollEnd() {
+ var last_changed_frame = 0;
+ var last_x = window.scrollX;
+ var last_y = window.scrollY;
+ return new Promise((resolve, reject) => {
+ function tick(frames) {
+ // We requestAnimationFrame either for 500 frames or until 20 frames with
+ // no change have been observed.
+ if (frames >= 500 || frames - last_changed_frame > 20) {
+ resolve();
+ } else {
+ if (window.scrollX != last_x || window.scrollY != last_y) {
+ last_changed_frame = frames;
+ last_x = window.scrollX;
+ last_y = window.scrollY;
+ }
+ requestAnimationFrame(tick.bind(null, frames + 1));
+ }
+ }
+ tick(0);
+ });
+}
+
+// When testing manually, we need an additional frame at beginning
+// to trigger the effect.
+requestAnimationFrame(() => {
+promise_test(t => {
+ window.scrollTo(0, 0);
+ var expected_x = content.offsetLeft + content_width - window_width;
+ var expected_y = content.offsetTop + content_height - window_height;
+ assert_not_equals(window.scrollX, expected_x, "scrollX");
+ assert_not_equals(window.scrollY, expected_y, "scrollY");
+ content.scrollIntoView({behavior: "smooth", block: "nearest", inline:
+"nearest"});
+ return waitForScrollEnd().then(() => {
+ assert_approx_equals(window.scrollX, expected_x, 1, "scrollX");
+ assert_approx_equals(window.scrollY, expected_y, 1, "scrollY");
+ });
+}, "Smooth scrollIntoView should scroll the element to the 'nearest' position");
+
+promise_test(t => {
+ window.scrollTo(0, 0);
+ var expected_x = content.offsetLeft;
+ var expected_y = content.offsetTop;
+ assert_not_equals(window.scrollX, expected_x, "scrollX");
+ assert_not_equals(window.scrollY, expected_y, "scrollY");
+ content.scrollIntoView({behavior: "smooth", block: "start", inline:
+"start"});
+ return waitForScrollEnd().then(() => {
+ assert_approx_equals(window.scrollX, expected_x, 1, "scrollX");
+ assert_approx_equals(window.scrollY, expected_y, 1, "scrollY");
+ });
+}, "Smooth scrollIntoView should scroll the element to the 'start' position");
+
+promise_test(t => {
+ window.scrollTo(0, 0);
+ var expected_x = content.offsetLeft + (content_width - window_width) / 2;
+ var expected_y = content.offsetTop + (content_height - window_height) / 2;
+ assert_not_equals(window.scrollX, expected_x, "scrollX");
+ assert_not_equals(window.scrollY, expected_y, "scrollY");
+ content.scrollIntoView({behavior: "smooth", block: "center", inline:
+"center"});
+ return waitForScrollEnd().then(() => {
+ assert_approx_equals(window.scrollX, expected_x, 1, "scrollX");
+ assert_approx_equals(window.scrollY, expected_y, 1, "scrollY");
+ });
+}, "Smooth scrollIntoView should scroll the element to the 'center' position");
+
+promise_test(t => {
+ window.scrollTo(0, 0);
+ var expected_x = content.offsetLeft + content_width - window_width;
+ var expected_y = content.offsetTop + content_height - window_height;
+ assert_not_equals(window.scrollX, expected_x, "scrollX");
+ assert_not_equals(window.scrollY, expected_y, "scrollY");
+ content.scrollIntoView({behavior: "smooth", block: "end", inline:
+"end"});
+ return waitForScrollEnd().then(() => {
+ assert_approx_equals(window.scrollX, expected_x, 1, "scrollX");
+ assert_approx_equals(window.scrollY, expected_y, 1, "scrollY");
+ });
+}, "Smooth scrollIntoView should scroll the element to the 'end' position");
+
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-stuck.tentative.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-stuck.tentative.html
new file mode 100644
index 0000000000..115b8ff9fb
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-stuck.tentative.html
@@ -0,0 +1,52 @@
+<!doctype html>
+<meta charset="utf-8">
+<meta name="viewport" content="width=device-width,minimum-scale=1">
+<title>CSSOM View - scrollIntoView doesn't consider scroll-padding when target is stuck</title>
+<link rel="author" title="Emilio Cobos Álvarez" href="mailto:emilio@crisal.io">
+<link rel="author" title="Mozilla" href="https://mozilla.org">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview">
+<link rel="help" href="https://drafts.csswg.org/css-scroll-snap-1/#scroll-padding">
+<link rel="help" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1795661">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+body { margin: 0 }
+:root { overflow: hidden }
+#container {
+ height: 100vh;
+ overflow: auto;
+ scroll-padding-top: 100px;
+}
+#sticky {
+ position: sticky;
+ background: Canvas;
+ top: 0;
+}
+#content {
+ height: 500vh;
+ background-image: linear-gradient(green, purple);
+}
+</style>
+<div id="container">
+ <div id="sticky">
+ <input type=text>
+ </div>
+ <div id="content"></div>
+</div>
+<script>
+let container = document.getElementById("container");
+let sticky = document.getElementById("sticky");
+test(() => {
+ // Scroll to the bottom.
+ container.scrollTo(0, 100000);
+
+ let scrollTop = container.scrollTop;
+
+ assert_not_equals(scrollTop, 0, "Should have scrolled");
+
+ // Focus on the stuck input. We shouldn't scroll up.
+ sticky.querySelector("input").scrollIntoView();
+
+ assert_equals(scrollTop, container.scrollTop, "Shouldn't have scrolled");
+});
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-svg-shape.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-svg-shape.html
new file mode 100644
index 0000000000..899f94671a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-svg-shape.html
@@ -0,0 +1,33 @@
+<!DOCTYPE HTML>
+<title>scrollIntoView on an SVG shape element</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview">
+<meta name="viewport" content="user-scalable=no">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<svg width="8000" height="8000">
+ <rect width="100" height="100" fill="blue" y="1950" id="geometry"/>
+ <rect width="100" height="100" fill="blue" transform="translate(0, 2950)"
+ id="translated"/>
+ <rect width="100" height="100" fill="blue" transform="rotate(45, 50, 3950)"
+ id="rotated"/>
+</svg>
+<script>
+add_completion_callback(() => {
+ document.querySelector("svg").remove();
+ window.scrollTo(0, 0);
+});
+
+for (let id of [ "geometry", "translated", "rotated" ]) {
+ test(t => {
+ let target = document.getElementById(id);
+ window.scrollTo(0, 0);
+ let bounds = target.getBoundingClientRect();
+ let expected = { x: bounds.left, y: bounds.top };
+ assert_not_equals(window.scrollX, expected.x, "x before scroll");
+ assert_not_equals(window.scrollY, expected.y, "y before scroll");
+ target.scrollIntoView({ block: "start", inline: "start" });
+ assert_approx_equals(window.scrollX, expected.x, 1, "x after scroll");
+ assert_approx_equals(window.scrollY, expected.y, 1, "y after scroll");
+ }, document.title + ", " + id);
+}
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-lr-writing-mode-and-rtl-direction.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-lr-writing-mode-and-rtl-direction.html
new file mode 100644
index 0000000000..3301141ee0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-lr-writing-mode-and-rtl-direction.html
@@ -0,0 +1,112 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView considers vertical-lr and rtl direction</title>
+<meta charset="utf-8">
+<link rel="author" title="Cathie Chen" href="mailto:cathiechen@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scroll-an-element">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<style>
+.box {
+ float: left;
+ width: 200px;
+ height: 200px;
+}
+#scroller {
+ writing-mode: vertical-lr;
+ direction: rtl;
+ overflow: scroll;
+ width: 300px;
+ height: 300px;
+}
+#container{
+ width: 600px;
+ height: 600px;
+}
+#target {
+ background-color: #ff0;
+}
+</style>
+<body>
+<div id="scroller">
+ <div id="container">
+ <!-- ROW-1 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-2 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box" id="target"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-3 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+ </div>
+</div>
+
+<script>
+// In vertical-lr mode and rtl direction, X corresponds to the block axis
+// and is oriented rightward. Y corresponds to the inline axis and is oriented
+// upward. So the beginning edges are the bottom and left edges and the ending
+// edges are the top and right edges.
+
+// According to the spec, y be min(0, max(y, element padding edge height - element scrolling area height)).
+// So y is nonpositive and decreases upward.
+
+// This assumes that the horizontal scrollbar
+// is on the bottom side and the vertical scrollbar is on the right side.
+
+var target = document.getElementById("target");
+var scroller = document.getElementById("scroller");
+var scrollbar_width = scroller.offsetWidth - scroller.clientWidth;
+
+var scroller_width = scroller.offsetWidth;
+var scroller_height = scroller.offsetHeight;
+var box_width = target.offsetWidth;
+var box_height = target.offsetHeight;
+
+var expectedX = {
+ blockStart: box_width,
+ blockCenter: (3*box_width - scroller_width)/2 + scrollbar_width/2,
+ blockEnd: 2*box_width - scroller_width + scrollbar_width,
+};
+
+var expectedY = {
+ inlineStart: -box_height,
+ inlineCenter: -((3*box_height - scroller_height)/2) - scrollbar_width/2,
+ inlineEnd: -(2*box_height - scroller_height) - scrollbar_width,
+};
+
+[
+ [{block: "start", inline: "start"}, expectedX.blockStart, expectedY.inlineStart],
+ [{block: "start", inline: "center"}, expectedX.blockStart, expectedY.inlineCenter],
+ [{block: "start", inline: "end"}, expectedX.blockStart, expectedY.inlineEnd],
+ [{block: "center", inline: "start"}, expectedX.blockCenter, expectedY.inlineStart],
+ [{block: "center", inline: "center"}, expectedX.blockCenter, expectedY.inlineCenter],
+ [{block: "center", inline: "end"}, expectedX.blockCenter, expectedY.inlineEnd],
+ [{block: "end", inline: "start"}, expectedX.blockEnd, expectedY.inlineStart],
+ [{block: "end", inline: "center"}, expectedX.blockEnd, expectedY.inlineCenter],
+ [{block: "end", inline: "end"}, expectedX.blockEnd, expectedY.inlineEnd],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ scroller.scrollTo(0, 0);
+ target.scrollIntoView(input);
+ assert_approx_equals(scroller.scrollLeft, expectedX, 0.5, "scrollX");
+ assert_approx_equals(scroller.scrollTop, expectedY, 0.5, "scrollY");
+ }, `scrollIntoView(${JSON.stringify(input)})`);
+})
+
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-lr-writing-mode.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-lr-writing-mode.html
new file mode 100644
index 0000000000..9242a49c0b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-lr-writing-mode.html
@@ -0,0 +1,107 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView considers vertical-lr writing mode</title>
+<meta charset="utf-8">
+<link rel="author" title="Cathie Chen" href="mailto:cathiechen@igalia.com">
+<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>
+.box {
+ float: left;
+ width: 200px;
+ height: 200px;
+}
+#scroller {
+ writing-mode: vertical-lr;
+ overflow: scroll;
+ width: 300px;
+ height: 300px;
+}
+#container{
+ width: 600px;
+ height: 600px;
+}
+#target {
+ background-color: #ff0;
+}
+</style>
+<body>
+<div id="scroller">
+ <div id="container">
+ <!-- ROW-1 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-2 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box" id="target"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-3 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+ </div>
+</div>
+
+<script>
+// In vertical-lr mode, X corresponds to the block axis and is oriented
+// rightward. Y corresponds to the inline axis and is oriented downward.
+// So the beginning edges are the top and left edges and the ending
+// edges are the bottom and right edges.
+
+// This assumes that the horizontal scrollbar is on the bottom side and
+// the vertical scrollbar is on the right side.
+
+var target = document.getElementById("target");
+var scroller = document.getElementById("scroller");
+var scrollbar_width = scroller.offsetWidth - scroller.clientWidth;
+
+var scroller_width = scroller.offsetWidth;
+var scroller_height = scroller.offsetHeight;
+var box_width = target.offsetWidth;
+var box_height = target.offsetHeight;
+
+var expectedX = {
+ blockStart: box_width,
+ blockCenter: (3*box_width - scroller_width)/2 + (scrollbar_width/2),
+ blockEnd: (2*box_width) - scroller_width + scrollbar_width,
+};
+
+var expectedY = {
+ inlineStart: box_height,
+ inlineCenter: ((3*box_height - scroller_height)/2) + (scrollbar_width/2),
+ inlineEnd: ((2*box_height) - scroller_height) + scrollbar_width,
+};
+
+[
+ [{block: "start", inline: "start"}, expectedX.blockStart, expectedY.inlineStart],
+ [{block: "start", inline: "center"}, expectedX.blockStart, expectedY.inlineCenter],
+ [{block: "start", inline: "end"}, expectedX.blockStart, expectedY.inlineEnd],
+ [{block: "center", inline: "start"}, expectedX.blockCenter, expectedY.inlineStart],
+ [{block: "center", inline: "center"}, expectedX.blockCenter, expectedY.inlineCenter],
+ [{block: "center", inline: "end"}, expectedX.blockCenter, expectedY.inlineEnd],
+ [{block: "end", inline: "start"}, expectedX.blockEnd, expectedY.inlineStart],
+ [{block: "end", inline: "center"}, expectedX.blockEnd, expectedY.inlineCenter],
+ [{block: "end", inline: "end"}, expectedX.blockEnd, expectedY.inlineEnd],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ scroller.scrollTo(0, 0);
+ target.scrollIntoView(input);
+ assert_approx_equals(scroller.scrollLeft, expectedX, 0.5, "scrollX");
+ assert_approx_equals(scroller.scrollTop, expectedY, 0.5, "scrollY");
+ }, `scrollIntoView(${JSON.stringify(input)})`);
+})
+
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-rl-writing-mode.html b/testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-rl-writing-mode.html
new file mode 100644
index 0000000000..dc5f3e2cdb
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollIntoView-vertical-rl-writing-mode.html
@@ -0,0 +1,117 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView considers vertical-rl writing mode</title>
+<meta charset="utf-8">
+<link rel="author" title="Suneel Kota" href="mailto:suneel.kota@samsung.com">
+<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>
+.box {
+ float: left;
+ width: 200px;
+ height: 200px;
+}
+#scroller {
+ writing-mode: vertical-rl;
+ overflow: scroll;
+ width: 300px;
+ height: 300px;
+}
+#container{
+ width: 600px;
+ height: 600px;
+}
+#target {
+ background-color: #ff0;
+}
+</style>
+<body>
+<div id="scroller">
+ <div id="container">
+ <!-- ROW-1 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-2 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box" id="target"></div>
+ <div class="box"></div>
+ </div>
+
+ <!-- ROW-3 -->
+ <div class="row">
+ <div class="box"></div>
+ <div class="box"></div>
+ <div class="box"></div>
+ </div>
+ </div>
+</div>
+
+<script>
+// In vertical-rl mode, X corresponds to the block axis and is oriented
+// leftward. Y corresponds to the inline axis and is oriented downward.
+// So the beginning edges are the top and right edges and the ending
+// edges are the bottom and left edges.
+
+// This assumes that the horizontal scrollbar is on the bottom side.
+
+var target = document.getElementById("target");
+var scroller = document.getElementById("scroller");
+var scrollbar_width = scroller.offsetWidth - scroller.clientWidth;
+
+var scroller_width = scroller.offsetWidth;
+var scroller_height = scroller.offsetHeight;
+var box_width = target.offsetWidth;
+var box_height = target.offsetHeight;
+
+var expectedX;
+// If scroll bar is on the left side, scroller.scrollLeft won't get bigger than 0,
+scroller.scrollLeft = scrollbar_width;
+if (scroller.scrollLeft == 0) {
+ expectedX = {
+ blockStart: -box_width,
+ blockCenter: -(((3*box_width - scroller_width)/2) + (scrollbar_width/2)),
+ blockEnd: -(((2*box_width) - scroller_width) + scrollbar_width),
+ };
+} else {
+ expectedX = {
+ blockStart: -(box_width - scrollbar_width),
+ blockCenter: -(((3*box_width - scroller_width)/2) + (scrollbar_width/2) - scrollbar_width),
+ blockEnd: -((2*box_width) - scroller_width),
+ };
+}
+
+var expectedY = {
+ inlineStart: box_height,
+ inlineCenter: ((3*box_height - scroller_height)/2) + (scrollbar_width/2),
+ inlineEnd: ((2*box_height) - scroller_height) + scrollbar_width,
+};
+
+[
+ [{block: "start", inline: "start"}, expectedX.blockStart, expectedY.inlineStart],
+ [{block: "start", inline: "center"}, expectedX.blockStart, expectedY.inlineCenter],
+ [{block: "start", inline: "end"}, expectedX.blockStart, expectedY.inlineEnd],
+ [{block: "center", inline: "start"}, expectedX.blockCenter, expectedY.inlineStart],
+ [{block: "center", inline: "center"}, expectedX.blockCenter, expectedY.inlineCenter],
+ [{block: "center", inline: "end"}, expectedX.blockCenter, expectedY.inlineEnd],
+ [{block: "end", inline: "start"}, expectedX.blockEnd, expectedY.inlineStart],
+ [{block: "end", inline: "center"}, expectedX.blockEnd, expectedY.inlineCenter],
+ [{block: "end", inline: "end"}, expectedX.blockEnd, expectedY.inlineEnd],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ scroller.scrollTo(0, 0);
+ target.scrollIntoView(input);
+ assert_approx_equals(scroller.scrollLeft, expectedX, 0.5, "scrollX");
+ assert_approx_equals(scroller.scrollTop, expectedY, 0.5, "scrollY");
+ }, `scrollIntoView(${JSON.stringify(input)})`);
+})
+
+</script>
+
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollLeft-of-scroller-with-wider-scrollbar.html b/testing/web-platform/tests/css/cssom-view/scrollLeft-of-scroller-with-wider-scrollbar.html
new file mode 100644
index 0000000000..e308c7c004
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollLeft-of-scroller-with-wider-scrollbar.html
@@ -0,0 +1,16 @@
+<!DOCTYPE html>
+<title>The maximum value of scrollLeft shouldn't be affected by scrollbar even if it's wider than scroller</title>
+<meta charset="utf-8">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollleft">
+
+<div id="scroller" style="overflow: scroll; width: 5px;height: 300px;">
+ <div style="width:100px; height: 1px"></div>
+</div>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+ test(function() {
+ scroller.scrollLeft = 1000;
+ assert_equals(scroller.scrollLeft, 100);
+ }, "Test the maxmium value of scrollLeft");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollLeftTop.html b/testing/web-platform/tests/css/cssom-view/scrollLeftTop.html
new file mode 100644
index 0000000000..55b4013600
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollLeftTop.html
@@ -0,0 +1,136 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollLeft/scrollTop considers writing-mode and css direction</title>
+<meta charset="utf-8">
+<link rel="author" title="Cathie Chen" href="mailto:cathiechen@igalia.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scrolling-area-origin">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#scroll-an-element">
+<meta name="assert" content="This test verifies the assigned and extreme values of the scroll positions of an element.">
+
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+
+<head>
+ <style>
+ .scroller {
+ overflow: scroll;
+ width: 150px;
+ height: 100px;
+ }
+ .content {
+ width: 300px;
+ height: 400px;
+ }
+ .horizontal-tb {
+ writing-mode: horizontal-tb;
+ }
+ .vertical-lr {
+ writing-mode: vertical-lr;
+ }
+ .vertical-rl {
+ writing-mode: vertical-rl;
+ }
+ .rtl {
+ direction: rtl;
+ }
+ </style>
+</head>
+
+<body>
+ <h1>scrollLeft/scrollTop</h1>
+ <h2>writing-mode: horizontal-tb;</h2>
+ <div id="target_scroller" class="scroller horizontal-tb ltr">
+ <div id="target_content" class="content"></div>
+ </div>
+ <div class="scroller horizontal-tb rtl">
+ <div class="content"></div>
+ </div>
+ <h2>writing-mode: vertical-lr;</h2>
+ <div class="scroller vertical-lr ltr">
+ <div class="content"></div>
+ </div>
+ <div class="scroller vertical-lr rtl">
+ <div class="content"></div>
+ </div>
+ <h2>writing-mode: vertical-rl;</h2>
+ <div class="scroller vertical-rl ltr">
+ <div class="content"></div>
+ </div>
+ <div class="scroller vertical-rl rtl">
+ <div class="content"></div>
+ </div>
+
+ <script>
+ var scroller = document.querySelector("#target_scroller");
+ var content = document.querySelector("#target_content");
+ var scrollbar_width = scroller.offsetWidth - scroller.clientWidth;
+ var scroller_width = scroller.offsetWidth;
+ var scroller_height = scroller.offsetHeight;
+ var content_width = content.offsetWidth;
+ var content_height = content.offsetHeight;
+
+ expectedScrollTop = content_height - scroller_height + scrollbar_width;
+ expectedScrollLeft = content_width - scroller_width + scrollbar_width;
+
+ const epsilon = 0.5;
+ test(() => {
+ var scroller = document.querySelector(".horizontal-tb.ltr");
+ assert_approx_equals(scroller.scrollLeft, 0, epsilon, "initial scrollLeft");
+ assert_approx_equals(scroller.scrollTop, 0, epsilon, "initial scrollTop");
+ scroller.scrollLeft = 2*content_width;
+ scroller.scrollTop = 2*content_height;
+ assert_approx_equals(scroller.scrollLeft, expectedScrollLeft, epsilon, "ending scrollLeft");
+ assert_approx_equals(scroller.scrollTop, expectedScrollTop, epsilon, "ending scrollTop");
+ }, `writing-mode:horizontal-tb; direction:ltr`);
+
+ test(() => {
+ var scroller = document.querySelector(".horizontal-tb.rtl");
+ assert_approx_equals(scroller.scrollLeft, 0, epsilon, "initial scrollLeft");
+ assert_approx_equals(scroller.scrollTop, 0, epsilon, "initial scrollTop");
+ scroller.scrollLeft = -2*content_width;
+ scroller.scrollTop = 2*content_height;
+ assert_approx_equals(scroller.scrollLeft, -expectedScrollLeft, epsilon, "ending scrollLeft");
+ assert_approx_equals(scroller.scrollTop, expectedScrollTop, epsilon, "ending scrollTop");
+ }, `writing-mode:horizontal-tb; direction:rtl`);
+
+ test(() => {
+ var scroller = document.querySelector(".vertical-lr.ltr");
+ assert_approx_equals(scroller.scrollLeft, 0, epsilon, "initial scrollLeft");
+ assert_approx_equals(scroller.scrollTop, 0, epsilon, "initial scrollTop");
+ scroller.scrollLeft = 2*content_width;
+ scroller.scrollTop = 2*content_height;
+ assert_approx_equals(scroller.scrollLeft, expectedScrollLeft, epsilon, "ending scrollLeft");
+ assert_approx_equals(scroller.scrollTop, expectedScrollTop, epsilon, "ending scrollTop");
+ }, `writing-mode:vertical-lr; direction:ltr`);
+
+ test(() => {
+ var scroller = document.querySelector(".vertical-lr.rtl");
+ assert_approx_equals(scroller.scrollLeft, 0, epsilon, "initial scrollLeft");
+ assert_approx_equals(scroller.scrollTop, 0, epsilon, "initial scrollTop");
+ scroller.scrollLeft = 2*content_width;
+ scroller.scrollTop = -2*content_height;
+ assert_approx_equals(scroller.scrollLeft, expectedScrollLeft, epsilon, "ending scrollLeft");
+ assert_approx_equals(scroller.scrollTop, -expectedScrollTop, epsilon, "ending scrollTop");
+ }, `writing-mode:vertical-lr; direction:rtl`);
+
+ test(() => {
+ var scroller = document.querySelector(".vertical-rl.ltr");
+ assert_approx_equals(scroller.scrollLeft, 0, epsilon, "initial scrollLeft");
+ assert_approx_equals(scroller.scrollTop, 0, epsilon, "initial scrollTop");
+ scroller.scrollLeft = -2*content_width;
+ scroller.scrollTop = 2*content_height;
+ assert_approx_equals(scroller.scrollLeft, -expectedScrollLeft, epsilon, "ending scrollLeft");
+ assert_approx_equals(scroller.scrollTop, expectedScrollTop, epsilon, "ending scrollTop");
+ }, `writing-mode:vertical-rl; direction:ltr`);
+
+ test(() => {
+ var scroller = document.querySelector(".vertical-rl.rtl");
+ assert_approx_equals(scroller.scrollLeft, 0, epsilon, "initial scrollLeft");
+ assert_approx_equals(scroller.scrollTop, 0, epsilon, "initial scrollTop");
+ scroller.scrollLeft = -2*content_width;
+ scroller.scrollTop = -2*content_height;
+ assert_approx_equals(scroller.scrollLeft, -expectedScrollLeft, epsilon, "ending scrollLeft");
+ assert_approx_equals(scroller.scrollTop, -expectedScrollTop, epsilon, "ending scrollTop");
+ }, `writing-mode:vertical-rl; direction:rtl`);
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollTop-display-change-ref.html b/testing/web-platform/tests/css/cssom-view/scrollTop-display-change-ref.html
new file mode 100644
index 0000000000..9f1259787b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollTop-display-change-ref.html
@@ -0,0 +1,8 @@
+<!doctype html>
+<meta charset=utf-8>
+<div id="scroller" style="height: 100px; overflow: scroll">
+ <div style="height: 1000px">
+ I should be visible.
+ </div>
+ I should not be visible.
+</div>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollTop-display-change.html b/testing/web-platform/tests/css/cssom-view/scrollTop-display-change.html
new file mode 100644
index 0000000000..916bfecfeb
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollTop-display-change.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Setting scrollTop to 0 immediately after toggling display from "none" on an element that had nonzero scrollTop before should work.</title>
+<link rel=match href="scrollTop-display-change-ref.html">
+<div id="scroller" style="height: 100px; overflow: scroll">
+ <div style="height: 1000px">
+ I should be visible.
+ </div>
+ I should not be visible.
+</div>
+<script>
+ scroller.scrollTop = 1000;
+ scroller.style.display = "none";
+ var win = scroller.scrollTop; // Force layout flush
+ scroller.style.display = "";
+ scroller.scrollTop = 0;
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollWidthHeight.xht b/testing/web-platform/tests/css/cssom-view/scrollWidthHeight.xht
new file mode 100644
index 0000000000..1893051767
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollWidthHeight.xht
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>CSS Test: CSSOM View scrollWidth and scrollHeight</title>
+ <link rel="author" title="Robert O'Callahan" href="mailto:robert@ocallahan.org" />
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#dom-element-scrollwidth" />
+ <meta name="flags" content="dom" />
+ <script src="/resources/testharness.js" type="text/javascript" />
+ <script src="/resources/testharnessreport.js" type="text/javascript" />
+ <style type="text/css"><![CDATA[
+ #elemSimple, #elemOverflow, #elemNestedOverflow {
+ border:1px solid black;
+ overflow:hidden;
+ width:200px;
+ height:40px;
+ padding-bottom:50px;
+ padding-right:40px;
+ }
+ #elemSimple > div {
+ background:yellow;
+ width:60px;
+ height:30px;
+ }
+ #elemOverflow > div {
+ background:yellow;
+ width:250px;
+ height:150px;
+ }
+ #elemNestedOverflow > div {
+ background:yellow;
+ width:60px;
+ height:30px;
+ }
+ #elemNestedOverflow > div > div {
+ background:blue;
+ width:250px;
+ height:150px;
+ }
+ ]]></style>
+ </head>
+ <body>
+ <noscript>Test not run - javascript required.</noscript>
+ <div id="log" />
+ <div id="elemSimple">
+ <div />
+ </div>
+ <div id="elemOverflow">
+ <div />
+ </div>
+ <div id="elemNestedOverflow">
+ <div>
+ <div />
+ </div>
+ </div>
+ <script type="text/javascript"><![CDATA[
+ var elemSimple = document.getElementById("elemSimple");
+ var elemOverflow = document.getElementById("elemOverflow");
+ var elemNestedOverflow = document.getElementById("elemNestedOverflow");
+
+ test(function(){
+ assert_equals(elemSimple.clientHeight, 90);
+ }, "elemSimple.clientHeight is the height of the padding edge");
+
+ test(function(){
+ assert_equals(elemSimple.scrollHeight, 90);
+ }, "elemSimple.scrollHeight is its clientHeight");
+
+ test(function(){
+ assert_equals(elemSimple.clientWidth, 240);
+ }, "elemSimple.clientWidth is the width of the padding edge");
+
+ test(function(){
+ assert_equals(elemSimple.scrollWidth, 240);
+ }, "elemSimple.scrollWidth is its clientWidth");
+
+ test(function(){
+ assert_equals(elemOverflow.clientHeight, 90);
+ }, "elemOverflow.clientHeight is the height of the padding edge");
+
+ test(function(){
+ assert_equals(elemOverflow.scrollHeight, 200);
+ }, "elemOverflow.scrollHeight is the height of its scrolled contents (including padding)");
+
+ test(function(){
+ assert_equals(elemOverflow.clientWidth, 240);
+ }, "elemOverflow.clientWidth is the width of the padding edge");
+
+ test(function(){
+ assert_equals(elemOverflow.scrollWidth, 290);
+ }, "elemOverflow.scrollHeight is the width of its scrolled contents (including padding)");
+
+ test(function(){
+ assert_equals(elemNestedOverflow.clientHeight, 90);
+ }, "elemNestedOverflow.clientHeight is the height of the padding edge");
+
+ test(function(){
+ assert_equals(elemNestedOverflow.scrollHeight, 150);
+ }, "elemNestedOverflow.scrollHeight is the height of its scrolled contents (ignoring padding)");
+
+ test(function(){
+ assert_equals(elemNestedOverflow.clientWidth, 240);
+ }, "elemNestedOverflow.clientWidth is the height of the padding edge");
+
+ test(function(){
+ assert_equals(elemNestedOverflow.scrollWidth, 250);
+ }, "elemNestedOverflow.scrollWidth is the width of its scrolled contents (ignoring padding)");
+
+ ]]></script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollWidthHeightWhenNotScrollable.xht b/testing/web-platform/tests/css/cssom-view/scrollWidthHeightWhenNotScrollable.xht
new file mode 100644
index 0000000000..f0fd373b1d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollWidthHeightWhenNotScrollable.xht
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml">
+ <head>
+ <title>CSS Test: CSSOM View scrollWidth/scrollHeight (for nonscrollable elements)</title>
+ <link rel="author" title="Robert O'Callahan" href="mailto:robert@ocallahan.org" />
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#dom-element-scrollwidth" />
+ <meta name="flags" content="dom" />
+ <script src="/resources/testharness.js" type="text/javascript" />
+ <script src="/resources/testharnessreport.js" type="text/javascript" />
+ <style type="text/css"><![CDATA[
+ #elemSimple, #elemOverflow, #elemNestedOverflow {
+ border:1px solid black;
+ width:200px;
+ height:40px;
+ padding-bottom:50px;
+ padding-right:40px;
+ }
+ #elemSimple > div {
+ background:yellow;
+ width:60px;
+ height:30px;
+ }
+ #elemOverflow > div {
+ background:yellow;
+ width:250px;
+ height:150px;
+ }
+ #elemNestedOverflow > div {
+ background:yellow;
+ width:60px;
+ height:30px;
+ }
+ #elemNestedOverflow > div > div {
+ background:blue;
+ width:250px;
+ height:150px;
+ }
+ ]]></style>
+ </head>
+ <body>
+ <noscript>Test not run - javascript required.</noscript>
+ <div id="log" />
+ <div id="elemSimple">
+ <div />
+ </div>
+ <div id="elemOverflow">
+ <div />
+ </div>
+ <div id="elemNestedOverflow">
+ <div>
+ <div />
+ </div>
+ </div>
+ <script type="text/javascript"><![CDATA[
+ var elemSimple = document.getElementById("elemSimple");
+ var elemOverflow = document.getElementById("elemOverflow");
+ var elemNestedOverflow = document.getElementById("elemNestedOverflow");
+
+ test(function(){
+ assert_equals(elemSimple.clientHeight, 90);
+ }, "elemSimple.clientHeight is the height of the padding edge");
+
+ test(function(){
+ assert_equals(elemSimple.scrollHeight, 90);
+ }, "elemSimple.scrollHeight is its clientHeight");
+
+ test(function(){
+ assert_equals(elemSimple.clientWidth, 240);
+ }, "elemSimple.clientWidth is the width of the padding edge");
+
+ test(function(){
+ assert_equals(elemSimple.scrollWidth, 240);
+ }, "elemSimple.scrollWidth is its clientWidth");
+
+ test(function(){
+ assert_equals(elemOverflow.clientHeight, 90);
+ }, "elemOverflow.clientHeight is the height of the padding edge");
+
+ /* This test differs from the spec. All major browsers give the result here, ignoring
+ the bottom padding.
+ */
+ test(function(){
+ assert_equals(elemOverflow.scrollHeight, 150);
+ }, "elemOverflow.scrollHeight is the height of its scrolled contents (ignoring padding, since we overflowed)");
+
+ test(function(){
+ }, "elemOverflow.clientWidth is the width of the padding edge");
+ assert_equals(elemOverflow.clientWidth, 240);
+
+ /* This test differs from the spec. All major browsers give the result here, ignoring
+ the right padding.
+ */
+ test(function(){
+ assert_equals(elemOverflow.scrollWidth, 250);
+ }, "elemOverflow.scrollHeight is the width of its scrolled contents (ignoring padding, since we overflowed)");
+
+ test(function(){
+ assert_equals(elemNestedOverflow.clientHeight, 90);
+ }, "elemNestedOverflow.clientHeight is the height of the padding edge");
+
+ /* This test differs from the spec. All major browsers give the result here, ignoring the
+ bottom padding.
+ */
+ test(function(){
+ assert_equals(elemNestedOverflow.scrollHeight, 150);
+ }, "elemNestedOverflow.scrollHeight is the height of its scrolled contents (ignoring padding, since we overflowed)");
+
+ test(function(){
+ assert_equals(elemNestedOverflow.clientWidth, 240);
+ }, "elemNestedOverflow.clientWidth is the height of the padding edge");
+
+ /* This test differs from the spec. All major browsers give the result here, ignoring
+ the right padding.
+ */
+ test(function(){
+ assert_equals(elemNestedOverflow.scrollWidth, 250);
+ }, "elemNestedOverflow.scrollWidth is the width of its scrolled contents (ignoring padding, since we overflowed)");
+
+ ]]></script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrolling-no-browsing-context.html b/testing/web-platform/tests/css/cssom-view/scrolling-no-browsing-context.html
new file mode 100644
index 0000000000..94357605b7
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrolling-no-browsing-context.html
@@ -0,0 +1,33 @@
+<!doctype html>
+<meta charset="utf-8">
+<title>cssom-view scrolling-no-browsing-context</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+setup({explicit_done:true});
+window.onload = function () {
+ test(function () {
+ var doc = document.implementation.createDocument("http://example.com/", "html", null);
+
+ var element = doc.createElement("tag")
+ assert_equals(element.scrollTop, 0, "scrollTop should be always 0");
+ assert_equals(element.scrollLeft, 0, "scrollLeft should be always 0");
+
+ element.scrollTop = 10;
+ element.scrollLeft = 10;
+ assert_equals(element.scrollTop, 0, "scrollTop should be always 0");
+ assert_equals(element.scrollLeft, 0, "scrollLeft should be always 0");
+
+ element.scroll(10, 10);
+ assert_equals(element.scrollTop, 0, "scrollTop should be always 0");
+ assert_equals(element.scrollLeft, 0, "scrollLeft should be always 0");
+
+ element.scrollTo(10, 10);
+ assert_equals(element.scrollTop, 0, "scrollTop should be always 0");
+ assert_equals(element.scrollLeft, 0, "scrollLeft should be always 0");
+
+ }, "Element get and set scrollTop, scrollLeft, scroll() and scrollTo() test");
+
+ done();
+};
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrolling-quirks-vs-nonquirks.html b/testing/web-platform/tests/css/cssom-view/scrolling-quirks-vs-nonquirks.html
new file mode 100644
index 0000000000..568f572ac2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrolling-quirks-vs-nonquirks.html
@@ -0,0 +1,220 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>cssom-view - scrolling quirks VS nonquirks mode</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+ iframe {
+ width: 300px;
+ height: 500px;
+ }
+</style>
+<iframe id="quirksframe"></iframe>
+<iframe id="nonquirksframe"></iframe>
+<div id="log"></div>
+<script>
+function setBodyContent(body) {
+ // Hide scrollbars and remove body margin to make measures more reliable.
+ body.style.overflow = "hidden";
+ body.style.margin = 0;
+
+ // Add an orange border to the body.
+ body.style.borderWidth = "10px 0px 0px 20px";
+ body.style.borderColor = "orange";
+ body.style.borderStyle = "solid";
+
+ // Create a 700x900 box with a green border.
+ body.innerHTML = "<div id='content' style='border-width: 30px 0px 0px 40px; border-style: solid; border-color: green; width: 660px; height: 870px; background: linear-gradient(135deg, red, blue);'></div>";
+}
+
+var quirksModeTest = async_test("Execution of tests in quirks mode");
+var quirksFrame = document.getElementById("quirksframe");
+quirksFrame.onload = function() {
+ var doc = quirksFrame.contentDocument;
+ setBodyContent(doc.body);
+ var content = doc.getElementById("content");
+
+ quirksModeTest.step(function () {
+ assert_equals(doc.compatMode, "BackCompat", "Should be in quirks mode.");
+ });
+
+ test(function () {
+ assert_equals(doc.scrollingElement, doc.body, "scrollingElement should be HTML body");
+ }, "scrollingElement in quirks mode");
+
+ test(function () {
+ doc.documentElement.scroll(50, 60);
+ assert_equals(doc.documentElement.scrollLeft, 0, "scrollLeft should be 0");
+ assert_equals(doc.documentElement.scrollTop, 0, "scrollTop should be 0");
+ }, "scroll() on the root element in quirks mode");
+
+ test(function () {
+ doc.documentElement.scrollBy(10, 20);
+ assert_equals(doc.documentElement.scrollLeft, 0, "scrollLeft should be 0");
+ assert_equals(doc.documentElement.scrollTop, 0, "scrollTop should be 0");
+ }, "scrollBy() on the root element in quirks mode");
+
+ test(function () {
+ doc.documentElement.scrollLeft = 70;
+ doc.documentElement.scrollTop = 80;
+ assert_equals(doc.documentElement.scrollLeft, 0, "scrollLeft should be 0");
+ assert_equals(doc.documentElement.scrollTop, 0, "scrollTop should be 0");
+ }, "scrollLeft/scrollTop on the root element in quirks mode");
+
+ test(function () {
+ assert_equals(doc.documentElement.scrollWidth, 720, "scrollWidth should be 720");
+ assert_equals(doc.documentElement.scrollHeight, 910, "scrollHeight should be 910");
+ }, "scrollWidth/scrollHeight on the root element in quirks mode");
+
+ test(function () {
+ assert_equals(doc.documentElement.clientWidth, 300, "clientWidth should be 300");
+ assert_equals(doc.documentElement.clientHeight, 910, "clientHeight should be 910");
+ }, "clientWidth/clientHeight on the root element in quirks mode");
+
+ test(function () {
+ doc.body.scroll(90, 100);
+ assert_equals(doc.body.scrollLeft, 90, "scrollLeft should be 90");
+ assert_equals(doc.body.scrollTop, 100, "scrollTop should be 100");
+ }, "scroll() on the HTML body element in quirks mode");
+
+ test(function () {
+ doc.body.scrollBy(10, 20);
+ assert_equals(doc.body.scrollLeft, 100, "scrollLeft should be 100");
+ assert_equals(doc.body.scrollTop, 120, "scrollTop should be 120");
+ }, "scrollBy() on the HTML body element in quirks mode");
+
+ test(function () {
+ doc.body.scrollLeft = 120;
+ doc.body.scrollTop = 110;
+ assert_equals(doc.body.scrollLeft, 120, "scrollLeft should be 120");
+ assert_equals(doc.body.scrollTop, 110, "scrollTop should be 110");
+ }, "scrollLeft/scrollTop on the HTML body element in quirks mode");
+
+ test(function () {
+ assert_equals(doc.body.scrollWidth, 720, "scrollWidth should be 720");
+ assert_equals(doc.body.scrollHeight, 910, "scrollHeight should be 910");
+ }, "scrollWidth/scrollHeight on the HTML body element in quirks mode");
+
+ test(function () {
+ assert_equals(doc.body.clientWidth, 300, "clientWidth should be 300");
+ assert_equals(doc.body.clientHeight, 500, "clientHeight should be 500");
+ }, "clientWidth/clientHeight on the HTML body element in quirks mode");
+
+ test(function () {
+ doc.scrollingElement.scroll(0, 0);
+ content.scrollLeft = 130;
+ content.scrollTop = 140;
+ assert_equals(content.scrollLeft, 0, "scrollLeft should be 0");
+ assert_equals(content.scrollTop, 0, "scrollTop should be 0");
+ }, "scrollLeft/scrollRight of the content in quirks mode");
+
+ test(function () {
+ assert_equals(content.scrollWidth, 660, "scrollWidth should be 660");
+ assert_equals(content.scrollHeight, 870, "scrollHeight should be 870");
+ }, "scrollWidth/scrollHeight of the content in quirks mode");
+
+ test(function () {
+ assert_equals(content.clientWidth, 660, "clientWidth should be 660");
+ assert_equals(content.clientHeight, 870, "clientHeight should be 870");
+ }, "clientWidth/clientHeight of the content in quirks mode");
+
+ quirksModeTest.done();
+}
+quirksFrame.src = URL.createObjectURL(new Blob([""], { type: "text/html" }));
+
+var nonQuirksModeTest = async_test("Execution of tests in non-quirks mode");
+var nonQuirksFrame = document.getElementById("nonquirksframe");
+nonQuirksFrame.onload = function() {
+ var doc = nonQuirksFrame.contentDocument;
+ setBodyContent(doc.body);
+ var content = doc.getElementById("content");
+
+ nonQuirksModeTest.step(function() {
+ assert_equals(doc.compatMode, "CSS1Compat", "Should be in standards mode.");
+ });
+
+ test(function () {
+ assert_equals(doc.scrollingElement, doc.documentElement, "scrollingElement should be documentElement");
+ }, "scrollingElement in non-quirks mode");
+
+ test(function () {
+ doc.documentElement.scroll(50, 60);
+ assert_equals(doc.documentElement.scrollLeft, 50, "scrollLeft should be 50");
+ assert_equals(doc.documentElement.scrollTop, 60, "scrollTop should be 60");
+ }, "scroll() on the root element in non-quirks mode");
+
+ test(function () {
+ doc.documentElement.scrollBy(10, 20);
+ assert_equals(doc.documentElement.scrollLeft, 60, "scrollLeft should be 60");
+ assert_equals(doc.documentElement.scrollTop, 80, "scrollTop should be 80");
+ }, "scrollBy() on the root element in non-quirks mode");
+
+ test(function () {
+ doc.documentElement.scrollLeft = 70;
+ doc.documentElement.scrollTop = 80;
+ assert_equals(doc.documentElement.scrollLeft, 70, "scrollLeft should be 70");
+ assert_equals(doc.documentElement.scrollTop, 80, "scrollTop should be 80");
+ }, "scrollLeft/scrollTop on the root element in non-quirks mode");
+
+ test(function () {
+ assert_equals(doc.documentElement.scrollWidth, 720, "scrollWidth should be 720");
+ assert_equals(doc.documentElement.scrollHeight, 910, "scrollHeight should be 910");
+ }, "scrollWidth/scrollHeight on the root element in non-quirks mode");
+
+ test(function () {
+ assert_equals(doc.documentElement.clientWidth, 300, "clientWidth should be 300");
+ assert_equals(doc.documentElement.clientHeight, 500, "clientHeight should be 500");
+ }, "clientWidth/clientHeight on the root element in non-quirks mode");
+
+ test(function () {
+ doc.body.scroll(90, 100);
+ assert_equals(doc.body.scrollLeft, 0, "scrollLeft should be 0");
+ assert_equals(doc.body.scrollTop, 0, "scrollTop should be 0");
+ }, "scroll() on the HTML body element in non-quirks mode");
+
+ test(function () {
+ doc.body.scrollBy(10, 20);
+ assert_equals(doc.body.scrollLeft, 0, "scrollLeft should be 0");
+ assert_equals(doc.body.scrollTop, 0, "scrollTop should be 0");
+ }, "scrollBy() on the HTML body element in non-quirks mode");
+
+ test(function () {
+ doc.body.scrollLeft = 120;
+ doc.body.scrollTop = 110;
+ assert_equals(doc.body.scrollLeft, 0, "scrollLeft should be 0");
+ assert_equals(doc.body.scrollTop, 0, "scrollTop should be 0");
+ }, "scrollLeft/scrollTop on the HTML body element in non-quirks mode");
+
+ test(function () {
+ assert_equals(doc.body.scrollWidth, 700, "scrollWidth should be 700");
+ assert_equals(doc.body.scrollHeight, 900, "scrollHeight should be 900");
+ }, "scrollWidth/scrollHeight on the HTML body element in non-quirks mode");
+
+ test(function () {
+ assert_equals(doc.body.clientWidth, 280, "clientWidth should be 280");
+ assert_equals(doc.body.clientHeight, 900, "clientHeight should be 900");
+ }, "clientWidth/clientHeight on the HTML body element in non-quirks mode");
+
+ test(function () {
+ doc.scrollingElement.scroll(0, 0);
+ content.scrollLeft = 130;
+ content.scrollTop = 140;
+ assert_equals(content.scrollLeft, 0, "scrollLeft should be 0");
+ assert_equals(content.scrollTop, 0, "scrollTop should be 0");
+ }, "scrollLeft/scrollRight of the content in non-quirks mode");
+
+ test(function () {
+ assert_equals(content.scrollWidth, 660, "scrollWidth should be 660");
+ assert_equals(content.scrollHeight, 870, "scrollHeight should be 870");
+ }, "scrollWidth/scrollHeight of the content in non-quirks mode");
+
+ test(function () {
+ assert_equals(content.clientWidth, 660, "clientWidth should be ");
+ assert_equals(content.clientHeight, 870, "clientHeight should be 870");
+ }, "clientWidth/clientHeight of the content in non-quirks mode");
+
+ nonQuirksModeTest.done();
+}
+nonQuirksFrame.src = URL.createObjectURL(new Blob(["<!doctype html>"], { type: "text/html" }));
+
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-001-ref.html b/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-001-ref.html
new file mode 100644
index 0000000000..683198a162
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-001-ref.html
@@ -0,0 +1,3 @@
+<!-- quirks mode -->
+<html style="overflow:scroll">
+<body style="overflow:scroll">The body box should have scrollbars.
diff --git a/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-001.html b/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-001.html
new file mode 100644
index 0000000000..344e299d0d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-001.html
@@ -0,0 +1,17 @@
+<!-- quirks mode -->
+<html>
+ <head>
+ <meta charset="utf-8">
+ <title>CSSOM View Test: Dynamically changing scrollingElement to html in quirks mode</title>
+ <link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+ <link rel="help" href="https://www.w3.org/TR/cssom-view-1/#dom-document-scrollingelement">
+ <link rel="match" href="scrollingElement-quirks-dynamic-001-ref.html">
+ <meta name="assert" content="Checks that setting the overflow on html to scroll will stop propagating body scrollbars to viewport.">
+ </head>
+ <body style="overflow:scroll">The body box should have scrollbars.
+ <script>
+ document.body.offsetTop; // force layout
+ document.documentElement.style.overflow = "scroll";
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-002-ref.html b/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-002-ref.html
new file mode 100644
index 0000000000..c8a783980f
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-002-ref.html
@@ -0,0 +1,2 @@
+<!-- quirks mode -->
+<body style="overflow:scroll">The body box should not have scrollbars.
diff --git a/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-002.html b/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-002.html
new file mode 100644
index 0000000000..8495be2f4b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollingElement-quirks-dynamic-002.html
@@ -0,0 +1,17 @@
+<!-- quirks mode -->
+<html style="overflow:scroll">
+ <head>
+ <meta charset="utf-8">
+ <title>CSSOM View Test: Dynamically changing scrollingElement to body in quirks mode</title>
+ <link rel="author" title="Rune Lillesveen" href="mailto:rune@opera.com">
+ <link rel="help" href="https://www.w3.org/TR/cssom-view-1/#dom-document-scrollingelement">
+ <link rel="match" href="scrollingElement-quirks-dynamic-002-ref.html">
+ <meta name="assert" content="Checks that setting the overflow on html to visible will propagate body scrollbars to viewport.">
+ </head>
+ <body style="overflow:scroll">The body box should not have scrollbars.
+ <script>
+ document.body.offsetTop; // force layout
+ document.documentElement.style.overflow = "visible";
+ </script>
+ </body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollingElement.html b/testing/web-platform/tests/css/cssom-view/scrollingElement.html
new file mode 100644
index 0000000000..408fa47be9
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollingElement.html
@@ -0,0 +1,126 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>cssom-view - scrollingElement</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+function makeDescription(rootDisplay, bodyDisplay) {
+ let a = [];
+ if (rootDisplay) {
+ a.push(`root ${rootDisplay}`);
+ }
+ if (bodyDisplay) {
+ a.push(`body ${bodyDisplay}`);
+ }
+ let s = a.join(", ");
+ if (s) {
+ s = ` (${s})`;
+ }
+ return s;
+}
+
+function quirksTest(rootDisplay, bodyDisplay) {
+ async_test(function() {
+ let quirksFrame = document.createElement("iframe");
+ quirksFrame.onload = this.step_func_done(function() {
+ var quirksDoc = quirksFrame.contentDocument;
+ assert_equals(quirksDoc.compatMode, "BackCompat", "Should be in quirks mode.");
+ assert_not_equals(quirksDoc.body, null, "Should have a body element");
+
+ quirksDoc.documentElement.style.display = rootDisplay;
+ quirksDoc.body.style.display = bodyDisplay;
+
+ // Tests for quirks mode document.
+ assert_equals(quirksDoc.scrollingElement, quirksDoc.body,
+ "scrollingElement in quirks mode should default to body element.");
+
+ quirksDoc.documentElement.style.overflow = "scroll";
+ quirksDoc.body.style.overflow = "scroll";
+ assert_equals(quirksDoc.scrollingElement, null,
+ "scrollingElement in quirks mode should be null if overflow of body and root element isn't visible.");
+ quirksDoc.documentElement.style.overflow = "visible";
+ assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
+ quirksDoc.documentElement.style.overflow = "scroll";
+ quirksDoc.body.style.overflow = "visible";
+ assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
+ quirksDoc.documentElement.style.overflow = "visible";
+ assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
+
+ quirksDoc.body.style.display = "none";
+ assert_equals(quirksDoc.scrollingElement, quirksDoc.body)
+ quirksDoc.body.style.display = "block";
+ assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
+
+ quirksDoc.documentElement.appendChild(quirksDoc.createElement("body"));
+ assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
+ assert_equals(quirksDoc.scrollingElement, quirksDoc.getElementsByTagName("body")[0]);
+ quirksDoc.documentElement.removeChild(quirksDoc.documentElement.lastChild);
+ assert_equals(quirksDoc.scrollingElement, quirksDoc.body);
+
+ quirksDoc.documentElement.removeChild(quirksDoc.body);
+ assert_equals(quirksDoc.scrollingElement, null);
+ quirksDoc.documentElement.appendChild(quirksDoc.createElementNS("foobarNS", "body"));
+ assert_equals(quirksDoc.scrollingElement, null);
+
+ quirksDoc.removeChild(quirksDoc.documentElement);
+ assert_equals(quirksDoc.scrollingElement, null);
+
+ quirksDoc.appendChild(quirksDoc.createElementNS("foobarNS", "html"));
+ quirksDoc.documentElement.appendChild(quirksDoc.createElement("body"));
+ assert_equals(quirksDoc.scrollingElement, null);
+
+ quirksDoc.removeChild(quirksDoc.documentElement);
+ quirksDoc.appendChild(quirksDoc.createElement("body"));
+ assert_equals(quirksDoc.scrollingElement, null);
+
+ quirksFrame.remove();
+ });
+ quirksFrame.src =
+ URL.createObjectURL(new Blob([], { type: "text/html" }));
+ document.body.append(quirksFrame);
+ }, `scrollingElement in quirks mode${makeDescription(rootDisplay, bodyDisplay)}`);
+}
+
+function nonQuirksTest(rootDisplay, bodyDisplay) {
+ async_test(function() {
+ let nonQuirksFrame = document.createElement("iframe");
+ nonQuirksFrame.onload = this.step_func_done(function() {
+ var nonQuirksDoc = nonQuirksFrame.contentDocument;
+ assert_equals(nonQuirksDoc.compatMode, "CSS1Compat", "Should be in standards mode.");
+ assert_not_equals(nonQuirksDoc.body, null, "Should have a body element");
+
+ nonQuirksDoc.documentElement.style.display = rootDisplay;
+ nonQuirksDoc.body.style.display = bodyDisplay;
+
+ assert_equals(nonQuirksDoc.scrollingElement, nonQuirksDoc.documentElement,
+ "scrollingElement in standards mode should be the document element.");
+
+ for (let rootOverflow of ["", "clip", "scroll", "hidden", "visible"]) {
+ for (let bodyOverflow of ["", "clip", "scroll", "hidden", "visible"]) {
+ nonQuirksDoc.documentElement.style.overflow = rootOverflow;
+ nonQuirksDoc.body.style.overflow = bodyOverflow;
+ assert_equals(nonQuirksDoc.scrollingElement, nonQuirksDoc.documentElement);
+ }
+ }
+
+ nonQuirksDoc.removeChild(nonQuirksDoc.documentElement);
+ assert_equals(nonQuirksDoc.scrollingElement, null);
+ nonQuirksDoc.appendChild(nonQuirksDoc.createElement("foobar"));
+ assert_equals(nonQuirksDoc.scrollingElement.localName, "foobar");
+
+ nonQuirksFrame.remove();
+ });
+ nonQuirksFrame.src =
+ URL.createObjectURL(new Blob([`<!doctype html>`], { type: "text/html" }));
+ document.body.append(nonQuirksFrame);
+ }, `scrollingElement in no-quirks mode ${makeDescription(rootDisplay, bodyDisplay)}`);
+}
+
+for (let rootDisplay of ["", "table"]) {
+ for (let bodyDisplay of ["", "table"]) {
+ quirksTest(rootDisplay, bodyDisplay);
+ nonQuirksTest(rootDisplay, bodyDisplay);
+ }
+}
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/scrollintoview.html b/testing/web-platform/tests/css/cssom-view/scrollintoview.html
new file mode 100644
index 0000000000..7a7ecfafcc
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/scrollintoview.html
@@ -0,0 +1,113 @@
+<!DOCTYPE html>
+<title>CSSOM View - scrollIntoView</title>
+<meta charset="utf-8">
+<meta name="viewport" content="initial-scale=1">
+<link rel="author" title="Chris Wu" href="mailto:pwx.frontend@gmail.com">
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#dom-element-scrollintoview">
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-operations">
+<link rel="help" href="https://webidl.spec.whatwg.org/#es-overloads">
+<meta name="flags" content="dom">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<style>
+body.running { margin: 0; padding: 4000px; overflow: hidden }
+body.running #testDiv {
+ width: 200px;
+ height: 200px;
+ background-color: lightgreen;
+}
+</style>
+<body class=running>
+<div id=testDiv></div>
+<div id="log"></div>
+<script>
+var testDiv = document.getElementById('testDiv');
+
+var expectedXLeft = 4000;
+var expectedXRight = 4000 - window.innerWidth + testDiv.clientWidth;
+var expectedXCenter = 4000 - (window.innerWidth / 2) + (testDiv.clientWidth / 2);
+
+var expectedYTop = 4000;
+var expectedYBottom = 4000 - window.innerHeight + testDiv.clientHeight;
+var expectedYCenter = 4000 - (window.innerHeight / 2) + (testDiv.clientHeight / 2);
+
+[
+ ["omitted argument", "nearest", expectedYTop],
+ [true, "nearest", expectedYTop],
+ [false, "nearest", expectedYBottom],
+ [undefined, "nearest", expectedYTop],
+ [null, "nearest", expectedYTop],
+ [{}, "nearest", expectedYTop],
+ [{block: "center", inline: "center"}, expectedXCenter, expectedYCenter],
+ [{block: "start", inline: "start"}, expectedXLeft, expectedYTop],
+ [{block: "end", inline: "end"}, expectedXRight, expectedYBottom],
+ [{block: "nearest", inline: "nearest"}, "nearest", "nearest"],
+].forEach(([input, expectedX, expectedY]) => {
+ test(() => {
+ window.scrollTo(0, 0);
+ testScrollIntoView(input);
+ var x = (expectedX === "nearest") ? expectedXRight : expectedX;
+ var y = (expectedY === "nearest") ? expectedYBottom : expectedY;
+ assert_approx_equals(window.scrollX, x, 0.5, 'scrollX');
+ assert_approx_equals(window.scrollY, y, 0.5, 'scrollY');
+ }, `scrollIntoView(${format_input(input)}) starting at left,top`);
+
+ test(() => {
+ window.scrollTo(0, 12000);
+ testScrollIntoView(input);
+ var x = (expectedX === "nearest") ? expectedXRight : expectedX;
+ var y = (expectedY === "nearest") ? expectedYTop : expectedY;
+ assert_approx_equals(window.scrollX, x, 0.5, 'scrollX');
+ assert_approx_equals(window.scrollY, y, 0.5, 'scrollY');
+ }, `scrollIntoView(${format_input(input)}) starting at left,bottom`);
+
+ test(() => {
+ window.scrollTo(12000, 0);
+ testScrollIntoView(input);
+ var x = (expectedX === "nearest") ? expectedXLeft : expectedX;
+ var y = (expectedY === "nearest") ? expectedYBottom : expectedY;
+ assert_approx_equals(window.scrollX, x, 0.5, 'scrollX');
+ assert_approx_equals(window.scrollY, y, 0.5, 'scrollY');
+ }, `scrollIntoView(${format_input(input)}) starting at right,top`);
+
+ test(() => {
+ window.scrollTo(12000, 12000);
+ testScrollIntoView(input);
+ var x = (expectedX === "nearest") ? expectedXLeft : expectedX;
+ var y = (expectedY === "nearest") ? expectedYTop : expectedY;
+ assert_approx_equals(window.scrollX, x, 0.5, 'scrollX');
+ assert_approx_equals(window.scrollY, y, 0.5, 'scrollY');
+ }, `scrollIntoView(${format_input(input)}) starting at right,bottom`);
+});
+
+function testScrollIntoView(input) {
+ if (input === "omitted argument") {
+ testDiv.scrollIntoView();
+ } else {
+ testDiv.scrollIntoView(input);
+ }
+}
+
+// This formats dict as a string suitable as test name.
+// format_value() is provided by testharness.js,
+// which also preserves sign for -0.
+function format_dict(dict) {
+ const props = [];
+ for (let prop in dict) {
+ props.push(`${prop}: ${format_value(dict[prop])}`);
+ }
+ return `{${props.join(', ')}}`;
+}
+
+function format_input(input) {
+ if (input === "omitted argument") {
+ return "";
+ } else if (input === null || typeof input !== "object") {
+ return format_value(input);
+ }
+ return format_dict(input);
+}
+
+document.body.classList.remove('running');
+window.scrollTo(0, 0);
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/support/1x1-green.png b/testing/web-platform/tests/css/cssom-view/support/1x1-green.png
new file mode 100644
index 0000000000..b98ca0ba0a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/1x1-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/1x1-lime.png b/testing/web-platform/tests/css/cssom-view/support/1x1-lime.png
new file mode 100644
index 0000000000..cb397fb090
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/1x1-lime.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/1x1-maroon.png b/testing/web-platform/tests/css/cssom-view/support/1x1-maroon.png
new file mode 100644
index 0000000000..3f86b07219
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/1x1-maroon.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/1x1-navy.png b/testing/web-platform/tests/css/cssom-view/support/1x1-navy.png
new file mode 100644
index 0000000000..9b9a03955b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/1x1-navy.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/1x1-red.png b/testing/web-platform/tests/css/cssom-view/support/1x1-red.png
new file mode 100644
index 0000000000..6bd73ac101
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/1x1-red.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/1x1-white.png b/testing/web-platform/tests/css/cssom-view/support/1x1-white.png
new file mode 100644
index 0000000000..dd43faec54
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/1x1-white.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/60x60-gg-rr.png b/testing/web-platform/tests/css/cssom-view/support/60x60-gg-rr.png
new file mode 100644
index 0000000000..84f5b2a4f1
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/60x60-gg-rr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/60x60-green.png b/testing/web-platform/tests/css/cssom-view/support/60x60-green.png
new file mode 100644
index 0000000000..b3c8cf3eb4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/60x60-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/README b/testing/web-platform/tests/css/cssom-view/support/README
new file mode 100644
index 0000000000..2e5f2ad073
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/README
@@ -0,0 +1,28 @@
+CSS Global Support Directory
+============================
+
+This directory contains common support files (such as images and external
+style sheets). These are sync'ed into the support directories of all our
+test suites. If you have test-suite-specific support files, please add
+them to the appropriate test-suite-specific support/ directory.
+
+If you add to a support/ directory, please run the tools/supportprop.py
+script from the top of the repository to cascade support files into the
+lower-level support directories.
+
+Description of the Common Support File Collection
+-------------------------------------------------
+
+The 1x1-* images are all exactly one pixel.
+
+The swatch-* images all use 15x15 cells.
+
+The square-* images all use 15x15 cells with one pixel borders.
+
+The pattern-* images use cells of various sizes:
+
+ pattern-grg-rgr-grg.png 20x20
+ pattern-rgr-grg-rgr.png 20x20
+ pattern-tr.png 15x15
+ pattern-grg-rrg-rgg.png 15x15
+
diff --git a/testing/web-platform/tests/css/cssom-view/support/a-green.css b/testing/web-platform/tests/css/cssom-view/support/a-green.css
new file mode 100644
index 0000000000..b0dbb071d5
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/a-green.css
@@ -0,0 +1 @@
+.a { color: green; }
diff --git a/testing/web-platform/tests/css/cssom-view/support/b-green.css b/testing/web-platform/tests/css/cssom-view/support/b-green.css
new file mode 100644
index 0000000000..a0473f5ca2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/b-green.css
@@ -0,0 +1 @@
+.b { color: green; } \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom-view/support/c-red.css b/testing/web-platform/tests/css/cssom-view/support/c-red.css
new file mode 100644
index 0000000000..d4ba5c64e9
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/c-red.css
@@ -0,0 +1 @@
+.c { color: red; } \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom-view/support/cat.png b/testing/web-platform/tests/css/cssom-view/support/cat.png
new file mode 100644
index 0000000000..85dd732481
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/cat.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/import-green.css b/testing/web-platform/tests/css/cssom-view/support/import-green.css
new file mode 100644
index 0000000000..537104e663
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/import-green.css
@@ -0,0 +1 @@
+.import { color: green; }
diff --git a/testing/web-platform/tests/css/cssom-view/support/import-red.css b/testing/web-platform/tests/css/cssom-view/support/import-red.css
new file mode 100644
index 0000000000..9945ef4711
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/import-red.css
@@ -0,0 +1 @@
+.import { color: red; }
diff --git a/testing/web-platform/tests/css/cssom-view/support/pattern-grg-rgr-grg.png b/testing/web-platform/tests/css/cssom-view/support/pattern-grg-rgr-grg.png
new file mode 100644
index 0000000000..9b88fbd811
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/pattern-grg-rgr-grg.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/pattern-grg-rrg-rgg.png b/testing/web-platform/tests/css/cssom-view/support/pattern-grg-rrg-rgg.png
new file mode 100644
index 0000000000..fcf4f3fd7d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/pattern-grg-rrg-rgg.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/pattern-rgr-grg-rgr.png b/testing/web-platform/tests/css/cssom-view/support/pattern-rgr-grg-rgr.png
new file mode 100644
index 0000000000..d454e3a630
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/pattern-rgr-grg-rgr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/pattern-tr.png b/testing/web-platform/tests/css/cssom-view/support/pattern-tr.png
new file mode 100644
index 0000000000..8b4b25364e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/pattern-tr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/ruler-h-50%.png b/testing/web-platform/tests/css/cssom-view/support/ruler-h-50%.png
new file mode 100644
index 0000000000..cf2eea6b43
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/ruler-h-50%.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/ruler-h-50px.png b/testing/web-platform/tests/css/cssom-view/support/ruler-h-50px.png
new file mode 100644
index 0000000000..9f46583665
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/ruler-h-50px.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/ruler-v-100px.png b/testing/web-platform/tests/css/cssom-view/support/ruler-v-100px.png
new file mode 100644
index 0000000000..a837eca222
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/ruler-v-100px.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/ruler-v-50px.png b/testing/web-platform/tests/css/cssom-view/support/ruler-v-50px.png
new file mode 100644
index 0000000000..8414102802
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/ruler-v-50px.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/scroll-behavior.js b/testing/web-platform/tests/css/cssom-view/support/scroll-behavior.js
new file mode 100644
index 0000000000..0a0968e025
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/scroll-behavior.js
@@ -0,0 +1,87 @@
+function observeScrolling(elements, callback) {
+ if (!Array.isArray(elements))
+ elements = [elements];
+ var lastChangedFrame = 0;
+ var lastLeft = new Map();
+ var lastTop = new Map();
+ elements.forEach((element) => {
+ lastLeft.set(element, element.scrollLeft);
+ lastTop.set(element, element.scrollTop);
+ });
+ function tick(frames) {
+ // We requestAnimationFrame either for 500 frames or until 20 frames with
+ // no change have been observed.
+ if (frames >= 500 || frames - lastChangedFrame > 20) {
+ callback(true);
+ } else {
+ var scrollHappened = elements.some((element) => {
+ return element.scrollLeft != lastLeft.get(element) || element.scrollTop != lastTop.get(element);
+ });
+ if (scrollHappened) {
+ lastChangedFrame = frames;
+ elements.forEach((element) => {
+ lastLeft.set(element, element.scrollLeft);
+ lastTop.set(element, element.scrollTop);
+ });
+ callback(false);
+ }
+ requestAnimationFrame(tick.bind(null, frames + 1));
+ }
+ }
+ tick(0);
+}
+
+function waitForScrollEnd(elements) {
+ return new Promise((resolve) => {
+ observeScrolling(elements, (done) => {
+ if (done)
+ resolve();
+ });
+ });
+}
+
+function resetScroll(scrollingElement) {
+ // Try various methods to ensure the element position is reset immediately.
+ scrollingElement.scrollLeft = 0;
+ scrollingElement.scrollTop = 0;
+ scrollingElement.scroll({left: 0, top: 0, behavior: "instant"});
+}
+
+function resetScrollForWindow(scrollingWindow) {
+ // Try various methods to ensure the element position is reset immediately.
+ scrollingWindow.document.scrollingElement.scrollLeft = 0;
+ scrollingWindow.document.scrollingElement.scrollTop = 0;
+ scrollingWindow.scroll({left: 0, top: 0, behavior: "instant"});
+}
+
+function setScrollBehavior(styledElement, className) {
+ styledElement.classList.remove("autoBehavior", "smoothBehavior");
+ styledElement.classList.add(className);
+}
+
+function scrollNode(scrollingElement, scrollFunction, behavior, elementToRevealLeft, elementToRevealTop) {
+ var args = {};
+ if (behavior)
+ args.behavior = behavior;
+ switch (scrollFunction) {
+ case "scrollIntoView":
+ args.inline = "start";
+ args.block = "start";
+ elementToReveal.scrollIntoView(args);
+ break;
+ default:
+ args.left = elementToRevealLeft;
+ args.top = elementToRevealTop;
+ scrollingElement[scrollFunction](args);
+ break;
+ }
+}
+
+function scrollWindow(scrollingWindow, scrollFunction, behavior, elementToRevealLeft, elementToRevealTop) {
+ var args = {};
+ if (behavior)
+ args.behavior = behavior;
+ args.left = elementToRevealLeft;
+ args.top = elementToRevealTop;
+ scrollingWindow[scrollFunction](args);
+}
diff --git a/testing/web-platform/tests/css/cssom-view/support/square-purple.png b/testing/web-platform/tests/css/cssom-view/support/square-purple.png
new file mode 100644
index 0000000000..0f522d7872
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/square-purple.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/square-teal.png b/testing/web-platform/tests/css/cssom-view/support/square-teal.png
new file mode 100644
index 0000000000..e567f51b91
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/square-teal.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/square-white.png b/testing/web-platform/tests/css/cssom-view/support/square-white.png
new file mode 100644
index 0000000000..5853cbb238
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/square-white.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/support/README b/testing/web-platform/tests/css/cssom-view/support/support/README
new file mode 100644
index 0000000000..ea8cb9ef35
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/support/README
@@ -0,0 +1,4 @@
+The swatch-green.png file in this directory is really a RED swatch,
+and the swatch-red.png file is really a green swatch.
+
+This directory is used to test relative URIs. \ No newline at end of file
diff --git a/testing/web-platform/tests/css/cssom-view/support/support/swatch-green.png b/testing/web-platform/tests/css/cssom-view/support/support/swatch-green.png
new file mode 100644
index 0000000000..1caf25c992
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/support/swatch-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/support/swatch-red.png b/testing/web-platform/tests/css/cssom-view/support/support/swatch-red.png
new file mode 100644
index 0000000000..0aa79b0c86
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/support/swatch-red.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/swatch-blue.png b/testing/web-platform/tests/css/cssom-view/support/swatch-blue.png
new file mode 100644
index 0000000000..bf2759634d
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/swatch-blue.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/swatch-green.png b/testing/web-platform/tests/css/cssom-view/support/swatch-green.png
new file mode 100644
index 0000000000..0aa79b0c86
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/swatch-green.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/swatch-lime.png b/testing/web-platform/tests/css/cssom-view/support/swatch-lime.png
new file mode 100644
index 0000000000..55fd7fdaed
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/swatch-lime.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/swatch-orange.png b/testing/web-platform/tests/css/cssom-view/support/swatch-orange.png
new file mode 100644
index 0000000000..d3cd498b52
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/swatch-orange.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/swatch-red.png b/testing/web-platform/tests/css/cssom-view/support/swatch-red.png
new file mode 100644
index 0000000000..1caf25c992
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/swatch-red.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/swatch-teal.png b/testing/web-platform/tests/css/cssom-view/support/swatch-teal.png
new file mode 100644
index 0000000000..0293ce89de
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/swatch-teal.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/swatch-white.png b/testing/web-platform/tests/css/cssom-view/support/swatch-white.png
new file mode 100644
index 0000000000..1a7d4323d7
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/swatch-white.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/swatch-yellow.png b/testing/web-platform/tests/css/cssom-view/support/swatch-yellow.png
new file mode 100644
index 0000000000..1591aa0e2e
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/swatch-yellow.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/test-bl.png b/testing/web-platform/tests/css/cssom-view/support/test-bl.png
new file mode 100644
index 0000000000..904e24e996
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/test-bl.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/test-br.png b/testing/web-platform/tests/css/cssom-view/support/test-br.png
new file mode 100644
index 0000000000..f413ff5c1a
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/test-br.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/test-inner-half-size.png b/testing/web-platform/tests/css/cssom-view/support/test-inner-half-size.png
new file mode 100644
index 0000000000..e473bf80ef
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/test-inner-half-size.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/test-outer.png b/testing/web-platform/tests/css/cssom-view/support/test-outer.png
new file mode 100644
index 0000000000..82eeace7fc
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/test-outer.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/test-tl.png b/testing/web-platform/tests/css/cssom-view/support/test-tl.png
new file mode 100644
index 0000000000..f6ac0ef7e8
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/test-tl.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/support/test-tr.png b/testing/web-platform/tests/css/cssom-view/support/test-tr.png
new file mode 100644
index 0000000000..59843ae54b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/support/test-tr.png
Binary files differ
diff --git a/testing/web-platform/tests/css/cssom-view/table-border-collapse-client-width-height.html b/testing/web-platform/tests/css/cssom-view/table-border-collapse-client-width-height.html
new file mode 100644
index 0000000000..a7a1a435b2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/table-border-collapse-client-width-height.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>CSSOM Test: Table with border-collapse: collapse's clientWidth/Height</title>
+<link rel="help" href="https://www.w3.org/TR/CSS2/tables.html#model" />
+<link rel="help" href="https://www.w3.org/TR/CSS2/tables.html#collapsing-borders" />
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface" />
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ table {
+ border-collapse: collapse;
+ }
+ td {
+ border: 8px solid #faa;
+ }
+</style>
+<table>
+ <tbody>
+ <tr>
+ <td>a</td>
+ <td>b</td>
+ </tr>
+ <tr>
+ <td>c</td>
+ <td>d</td>
+ </tr>
+ </tbody>
+</table>
+<script type="text/javascript">
+ var table = document.getElementsByTagName('table')[0];
+ test(function() {
+ assert_equals(table.clientWidth, table.offsetWidth);
+ assert_equals(table.clientHeight, table.offsetHeight);
+ }, "Table's clientWidth/Height and OffsetWidth/Height should be the same");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/table-border-separate-client-width-height.html b/testing/web-platform/tests/css/cssom-view/table-border-separate-client-width-height.html
new file mode 100644
index 0000000000..d960d713dc
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/table-border-separate-client-width-height.html
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>CSSOM Test: Table with border-collapse: separate's clientWidth/Height</title>
+<link rel="help" href="https://www.w3.org/TR/CSS2/tables.html#model" />
+<link rel="help" href="https://www.w3.org/TR/CSS2/tables.html#separated-borders" />
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface" />
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ table {
+ border-collapse: separate;
+ }
+ td {
+ border: 8px solid #faa;
+ }
+</style>
+<table>
+ <tbody>
+ <tr>
+ <td>a</td>
+ <td>b</td>
+ </tr>
+ <tr>
+ <td>c</td>
+ <td>d</td>
+ </tr>
+ </tbody>
+</table>
+<script type="text/javascript">
+ var table = document.getElementsByTagName('table')[0];
+ test(function() {
+ assert_equals(table.clientWidth, table.offsetWidth);
+ assert_equals(table.clientHeight, table.offsetHeight);
+ }, "Table's clientWidth/Height and OffsetWidth/Height should be the same");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/table-client-props.html b/testing/web-platform/tests/css/cssom-view/table-client-props.html
new file mode 100644
index 0000000000..4af06d6bf7
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/table-client-props.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>client* properties on tables</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id="test-target" style="position: absolute"></div>
+<script>
+ test(function() {
+ // Each test consists of four elements: the markup to use, the expected
+ // value of offsetWidth on the table, the expected value of offsetHeight
+ // on the table, and the description string.
+ var tests = [
+ [ `<table style="width: 20px; height: 30px">`,
+ 20, 30,
+ "Basic table" ],
+ [ `<table><caption style="width: 40px; height: 50px">`,
+ 40, 50,
+ "Basic caption" ],
+ [ `<table style="width: 20px; height: 30px">
+ <caption style="width: 10px; height: 20px">`,
+ 20, 50,
+ "Table and narrower caption" ],
+ [ `<table style="width: 20px; height: 30px">
+ <caption style="width: 40px; height: 20px">`,
+ 40, 50,
+ "Table and wider caption" ],
+ [ `<table style="width: 20px; height: 30px; padding: 1px 2px 3px 4px">`,
+ 20, 30,
+ "Table with padding" ],
+ [ `<table style="width: 20px; height: 30px; padding: 1px 2px 3px 4px;
+ box-sizing: content-box">`,
+ 26, 34,
+ "Table with padding and content-box sizing" ],
+ [ `<table style="width: 20px; height: 30px;
+ border-width: 1px 2px 3px 4px; border-style: solid;
+ border-collapse: separate; box-sizing: content-box">`,
+ 26, 34,
+ "Table with separated border" ],
+ [ `<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,
+ "Table with collapsed border" ],
+ [ `<table>
+ <caption style="width: 40px; height: 50px; padding: 1px 2px 3px 4px">`,
+ 46, 54,
+ "Caption with padding" ],
+ [ `<table>
+ <caption style="width: 40px; height: 50px;
+ border-width: 1px 2px 3px 4px; border-style: solid">`,
+ 46, 54,
+ "Caption with border" ],
+ [ `<table>
+ <caption style="width: 40px; height: 50px; margin: 1px 2px 3px 4px;">`,
+ 46, 54,
+ "Caption with margin" ],
+ [ `<table style="caption-side: bottom">
+ <caption style="width: 40px; height: 50px;">`,
+ 40, 50,
+ "Bottom caption" ],
+ ];
+
+ function target() {
+ return document.getElementById("test-target");
+ }
+
+ function table() {
+ return target().querySelector("table");
+ }
+
+ for (var t of tests) {
+ test(function() {
+ target().innerHTML = t[0];
+ assert_equals(table().clientWidth, t[1], t[3] + " clientWidth");
+ assert_equals(table().clientHeight, t[2], t[3] + " clientHeight");
+ assert_equals(table().clientLeft, 0, t[3] + " clientLeft");
+ assert_equals(table().clientTop, 0, t[3] + " clientTop");
+ }, t[3]);
+ }
+ }, "Overall test to make sure there are no exceptions");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/table-offset-props.html b/testing/web-platform/tests/css/cssom-view/table-offset-props.html
new file mode 100644
index 0000000000..43aac248f0
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/table-offset-props.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>offset* properties on tables</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extensions-to-the-htmlelement-interface">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id="test-target" style="position: absolute"></div>
+<script>
+ test(function() {
+ // Each test consists of four elements: the markup to use, the expected
+ // value of offsetWidth on the table, the expected value of offsetHeight
+ // on the table, and the description string.
+ var tests = [
+ [ `<table style="width: 20px; height: 30px">`,
+ 20, 30,
+ "Basic table" ],
+ [ `<table><caption style="width: 40px; height: 50px">`,
+ 40, 50,
+ "Basic caption" ],
+ [ `<table style="width: 20px; height: 30px">
+ <caption style="width: 10px; height: 20px">`,
+ 20, 50,
+ "Table and narrower caption" ],
+ [ `<table style="width: 20px; height: 30px">
+ <caption style="width: 40px; height: 20px">`,
+ 40, 50,
+ "Table and wider caption" ],
+ [ `<table style="width: 20px; height: 30px; padding: 1px 2px 3px 4px">`,
+ 20, 30,
+ "Table with padding" ],
+ [ `<table style="width: 20px; height: 30px; padding: 1px 2px 3px 4px;
+ box-sizing: content-box">`,
+ 26, 34,
+ "Table with padding and content-box sizing" ],
+ [ `<table style="width: 20px; height: 30px;
+ border-width: 1px 2px 3px 4px; border-style: solid;
+ border-collapse: separate; box-sizing: content-box">`,
+ 26, 34,
+ "Table with separated border" ],
+ [ `<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,
+ "Table with collapsed border" ],
+ [ `<table>
+ <caption style="width: 40px; height: 50px; padding: 1px 2px 3px 4px">`,
+ 46, 54,
+ "Caption with padding" ],
+ [ `<table>
+ <caption style="width: 40px; height: 50px;
+ border-width: 1px 2px 3px 4px; border-style: solid">`,
+ 46, 54,
+ "Caption with border" ],
+ [ `<table>
+ <caption style="width: 40px; height: 50px; margin: 1px 2px 3px 4px;">`,
+ 46, 54,
+ "Caption with margin" ],
+ [ `<table style="caption-side: bottom">
+ <caption style="width: 40px; height: 50px;">`,
+ 40, 50,
+ "Bottom caption" ],
+ ];
+
+ function target() {
+ return document.getElementById("test-target");
+ }
+
+ function table() {
+ return target().querySelector("table");
+ }
+
+ for (var t of tests) {
+ test(function() {
+ target().innerHTML = t[0];
+ assert_equals(table().offsetWidth, t[1], t[3] + " offsetWidth");
+ assert_equals(table().offsetHeight, t[2], t[3] + " offsetHeight");
+ assert_equals(table().offsetLeft, 0, t[3] + " offsetLeft");
+ assert_equals(table().offsetTop, 0, t[3] + " offsetTop");
+ }, t[3]);
+ }
+ }, "Overall test to make sure there are no exceptions");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/table-scroll-props.html b/testing/web-platform/tests/css/cssom-view/table-scroll-props.html
new file mode 100644
index 0000000000..fd7f0d347b
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/table-scroll-props.html
@@ -0,0 +1,83 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>scroll* properties on tables</title>
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface">
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<div id="test-target" style="position: absolute"></div>
+<script>
+ test(function() {
+ // Each test consists of four elements: the markup to use, the expected
+ // value of offsetWidth on the table, the expected value of offsetHeight
+ // on the table, and the description string.
+ var tests = [
+ [ `<table style="width: 20px; height: 30px">`,
+ 20, 30,
+ "Basic table" ],
+ [ `<table><caption style="width: 40px; height: 50px">`,
+ 40, 50,
+ "Basic caption" ],
+ [ `<table style="width: 20px; height: 30px">
+ <caption style="width: 10px; height: 20px">`,
+ 20, 50,
+ "Table and narrower caption" ],
+ [ `<table style="width: 20px; height: 30px">
+ <caption style="width: 40px; height: 20px">`,
+ 40, 50,
+ "Table and wider caption" ],
+ [ `<table style="width: 20px; height: 30px; padding: 1px 2px 3px 4px">`,
+ 20, 30,
+ "Table with padding" ],
+ [ `<table style="width: 20px; height: 30px; padding: 1px 2px 3px 4px;
+ box-sizing: content-box">`,
+ 26, 34,
+ "Table with padding and content-box sizing" ],
+ [ `<table style="width: 20px; height: 30px;
+ border-width: 1px 2px 3px 4px; border-style: solid;
+ border-collapse: separate; box-sizing: content-box">`,
+ 26, 34,
+ "Table with separated border" ],
+ [ `<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,
+ "Table with collapsed border" ],
+ [ `<table>
+ <caption style="width: 40px; height: 50px; padding: 1px 2px 3px 4px">`,
+ 46, 54,
+ "Caption with padding" ],
+ [ `<table>
+ <caption style="width: 40px; height: 50px;
+ border-width: 1px 2px 3px 4px; border-style: solid">`,
+ 46, 54,
+ "Caption with border" ],
+ [ `<table>
+ <caption style="width: 40px; height: 50px; margin: 1px 2px 3px 4px;">`,
+ 46, 54,
+ "Caption with margin" ],
+ [ `<table style="caption-side: bottom">
+ <caption style="width: 40px; height: 50px;">`,
+ 40, 50,
+ "Bottom caption" ],
+ ];
+
+ function target() {
+ return document.getElementById("test-target");
+ }
+
+ function table() {
+ return target().querySelector("table");
+ }
+
+ for (var t of tests) {
+ test(function() {
+ target().innerHTML = t[0];
+ assert_equals(table().scrollWidth, t[1], t[3] + " scrollWidth");
+ assert_equals(table().scrollHeight, t[2], t[3] + " scrollHeight");
+ assert_equals(table().scrollLeft, 0, t[3] + " scrollLeft");
+ assert_equals(table().scrollTop, 0, t[3] + " scrollTop");
+ }, t[3]);
+ }
+ }, "Overall test to make sure there are no exceptions");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/table-with-border-client-width-height.html b/testing/web-platform/tests/css/cssom-view/table-with-border-client-width-height.html
new file mode 100644
index 0000000000..4e9a5deed2
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/table-with-border-client-width-height.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>CSSOM Test: Table with its own specified border's clientWidth/Height</title>
+<link rel="help" href="https://www.w3.org/TR/CSS2/tables.html#model" />
+<link rel="help" href="https://www.w3.org/TR/CSS2/tables.html#separated-borders" />
+<link rel="help" href="https://drafts.csswg.org/cssom-view/#extension-to-the-element-interface" />
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<style>
+ table {
+ border: 8px solid #faa;
+ }
+</style>
+<table>
+ <tbody>
+ <tr>
+ <td>a</td>
+ <td>b</td>
+ </tr>
+ <tr>
+ <td>c</td>
+ <td>d</td>
+ </tr>
+ </tbody>
+</table>
+<script type="text/javascript">
+ var table = document.getElementsByTagName('table')[0];
+ test(function() {
+ assert_equals(table.clientWidth, table.offsetWidth);
+ assert_equals(table.clientHeight, table.offsetHeight);
+ }, "Table's clientWidth/Height and OffsetWidth/Height should be the same");
+</script>
diff --git a/testing/web-platform/tests/css/cssom-view/ttwf-js-cssomview-getclientrects-length.html b/testing/web-platform/tests/css/cssom-view/ttwf-js-cssomview-getclientrects-length.html
new file mode 100644
index 0000000000..bc87317456
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/ttwf-js-cssomview-getclientrects-length.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM View API Test: the length of getClientRects</title>
+ <link rel="author" title="simplezeroec" href="mailto:zhaolp0419@gmail.com">
+ <link rel="help" href="https://www.w3.org/TR/cssom-view/#dom-element-getclientrects">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="getClientRects will return rects of the correct number">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+
+</head>
+<body>
+ <div id="rectcontainer">
+ <button id="testBtn" >Test</button>
+ <a id="testLink">Test Link</a>
+ </div>
+ <div id="log"></div>
+ <script>
+
+ test(function(){assert_equals(document.getElementById("rectcontainer").getClientRects().length,1,"1 ClientRects should be returned");},"assert_length_of_getClientRects_from_Parent");
+ test(function(){assert_equals(document.getElementById("testBtn").getClientRects().length,1,"1 ClientRects should be returned");},"assert_length_of_getClientRects_from_Button");
+ test(function(){assert_equals(document.getElementById("testLink").getClientRects().length,1,"1 ClientRects should be returned");},"assert_length_of_getClientRects_from_Link");
+
+
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/window-screen-height-immutable.html b/testing/web-platform/tests/css/cssom-view/window-screen-height-immutable.html
new file mode 100644
index 0000000000..88dd068481
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/window-screen-height-immutable.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM View - 4.2 - screen.height immutability</title>
+ <link rel="author" title="Neils Christoffersen" href="mailto:neils.christoffersen@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#the-screen-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="screen.height is immutable">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="myDiv"></div>
+ <div id="log"></div>
+ <script>
+ var originalVal = window.screen.height;
+
+ // try to set window.screen.height
+ window.screen.height = 0;
+
+ // verify window.screen.height didn't change
+ test(function() {
+ assert_equals(window.screen.height, originalVal, "window.screen.height should be immutable")
+ }, "immutability test");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/window-screen-height.html b/testing/web-platform/tests/css/cssom-view/window-screen-height.html
new file mode 100644
index 0000000000..9bb7e528b8
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/window-screen-height.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM View - 4.2 - screen.height range tests</title>
+ <link rel="author" title="Neils Christoffersen" href="mailto:neils.christoffersen@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#the-screen-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="window.screen.height has sensible values">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="myDiv"></div>
+ <div id="log"></div>
+ <script>
+ var upperBound = 50000;
+
+ test(
+ function() {
+ assert_not_equals(window.screen.height, 0, "window.screen.height is zero")
+ }, "zero check"
+ );
+
+ test(
+ function() {
+ assert_true(window.screen.height > 0, "window.screen.height shouldn't be negative")
+ }, "positive check"
+ );
+
+ test(
+ function() {
+ assert_true(window.screen.height < upperBound, "window.screen.height shouldn't be so large")
+ }, "upper bound check"
+ );
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/window-screen-width-immutable.html b/testing/web-platform/tests/css/cssom-view/window-screen-width-immutable.html
new file mode 100644
index 0000000000..1415bfaca4
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/window-screen-width-immutable.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM View - 4.2 - screen.width immutability</title>
+ <link rel="author" title="Neils Christoffersen" href="mailto:neils.christoffersen@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#the-screen-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="screen.width is immutable">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="myDiv"></div>
+ <div id="log"></div>
+ <script>
+ var originalVal = window.screen.width;
+
+ // try to set window.screen.width
+ window.screen.width = 0;
+
+ // verify window.screen.width didn't change
+ test(function() {
+ assert_equals(window.screen.width, originalVal, "window.screen.width should be immutable")
+ }, "immutability test");
+ </script>
+</body>
+</html>
diff --git a/testing/web-platform/tests/css/cssom-view/window-screen-width.html b/testing/web-platform/tests/css/cssom-view/window-screen-width.html
new file mode 100644
index 0000000000..fdae2bc742
--- /dev/null
+++ b/testing/web-platform/tests/css/cssom-view/window-screen-width.html
@@ -0,0 +1,37 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>CSSOM View - 4.2 - screen.width range tests</title>
+ <link rel="author" title="Neils Christoffersen" href="mailto:neils.christoffersen@gmail.com">
+ <link rel="help" href="http://www.w3.org/TR/cssom-view/#the-screen-interface">
+ <meta name="flags" content="dom">
+ <meta name="assert" content="window.screen.width has sensible values">
+ <script src="/resources/testharness.js"></script>
+ <script src="/resources/testharnessreport.js"></script>
+</head>
+<body>
+ <div id="myDiv"></div>
+ <div id="log"></div>
+ <script>
+ var upperBound = 50000;
+
+ test(
+ function() {
+ assert_not_equals(window.screen.width, 0, "window.screen.width is zero")
+ }, "zero check"
+ );
+
+ test(
+ function() {
+ assert_true(window.screen.width > 0, "window.screen.width shouldn't be negative")
+ }, "positive check"
+ );
+
+ test(
+ function() {
+ assert_true(window.screen.width < upperBound, "window.screen.width shouldn't be so large")
+ }, "upper bound check"
+ );
+ </script>
+</body>
+</html>