Update web-platform-tests to revision b7a8b84debb42268ea95a45bdad8f727d1facdf7

This commit is contained in:
WPT Sync Bot 2019-03-21 21:40:20 -04:00
parent ba929208e4
commit 953dbda9a6
215 changed files with 6409 additions and 1644 deletions

File diff suppressed because it is too large Load diff

View file

@ -62,9 +62,6 @@
[Matching font-style: 'italic' should prefer 'italic' over 'oblique 20deg']
expected: FAIL
[Matching font-style: 'italic' should prefer 'oblique 30deg 60deg' over 'oblique 40deg 50deg']
expected: FAIL
[Matching font-style: 'italic' should prefer 'oblique 40deg 50deg' over 'oblique 5deg 10deg']
expected: FAIL
@ -149,9 +146,6 @@
[Matching font-style: 'oblique 20deg' should prefer 'oblique 30deg 60deg' over 'oblique 40deg 50deg']
expected: FAIL
[Matching font-style: 'oblique 20deg' should prefer 'oblique 40deg 50deg' over 'oblique 10deg']
expected: FAIL
[Matching font-style: 'oblique 20deg' should prefer 'oblique 0deg' over 'oblique -50deg -20deg']
expected: FAIL
@ -224,9 +218,6 @@
[Matching font-weight: '500' should prefer '450 460' over '400']
expected: FAIL
[Matching font-weight: '501' should prefer '501' over '502 510']
expected: FAIL
[Matching font-style: 'oblique -20deg' should prefer 'oblique -20deg' over 'oblique -60deg -40deg']
expected: FAIL
@ -326,3 +317,12 @@
[Matching font-style: 'oblique -21deg' should prefer 'oblique -10deg' over 'italic']
expected: FAIL
[Matching font-weight: '399' should prefer '340 360' over '200 300']
expected: FAIL
[Matching font-style: 'oblique 0deg' should prefer 'oblique 5deg' over 'oblique 15deg 20deg']
expected: FAIL
[Matching font-weight: '501' should prefer '500' over '450 460']
expected: FAIL

View file

@ -38,3 +38,6 @@
[Test @font-face matching for weight 500]
expected: FAIL
[Test @font-face matching for weight 400]
expected: FAIL

View file

@ -74,18 +74,3 @@
[opacity end]
expected: FAIL
[height end]
expected: FAIL
[border-top-width end]
expected: FAIL
[border-left-width end]
expected: FAIL
[border-bottom-width end]
expected: FAIL
[border-right-width end]
expected: FAIL

View file

@ -1,4 +0,0 @@
[navigation-unload-same-origin-fragment.html]
[Tests that a fragment navigation in the unload handler will not block the initial navigation]
expected: FAIL

View file

@ -0,0 +1,4 @@
[traverse_the_history_5.html]
[Multiple history traversals, last would be aborted]
expected: FAIL

View file

@ -1,5 +1,5 @@
[name-attribute.window.html]
expected: CRASH
expected: TIMEOUT
[cross-origin <frame name=>]
expected: TIMEOUT

View file

@ -1,10 +0,0 @@
[non-active-document.html]
[DOMParser]
expected: FAIL
[createHTMLDocument]
expected: FAIL
[<template>]
expected: FAIL

View file

@ -0,0 +1,4 @@
[DOMContentLoaded-defer.html]
[The end: DOMContentLoaded and defer scripts]
expected: FAIL

View file

@ -0,0 +1,11 @@
[Worker-custom-event.any.worker.html]
[Worker-custom-event.any.sharedworker.html]
[Worker-custom-event]
expected: FAIL
[Worker-custom-event.any.serviceworker.html]
[Worker-custom-event]
expected: FAIL

View file

@ -0,0 +1,11 @@
[Worker-replace-event-handler.any.sharedworker.html]
[Worker-replace-event-handler]
expected: FAIL
[Worker-replace-event-handler.any.worker.html]
[Worker-replace-event-handler.any.serviceworker.html]
[Worker-replace-event-handler]
expected: FAIL

View file

@ -0,0 +1,14 @@
[WorkerNavigator-hardware-concurrency.any.sharedworker.html]
[WorkerNavigator-hardware-concurrency]
expected: FAIL
[WorkerNavigator-hardware-concurrency.any.worker.html]
[Test worker navigator hardware concurrency.]
expected: FAIL
[WorkerNavigator-hardware-concurrency.any.serviceworker.html]
[WorkerNavigator-hardware-concurrency]
expected: FAIL

View file

@ -1,4 +1,5 @@
[005.html]
expected: ERROR
[dedicated worker in shared worker in dedicated worker]
expected: FAIL

View file

@ -1,5 +1,6 @@
[003.html]
type: testharness
expected: ERROR
[shared]
expected: FAIL

View file

@ -0,0 +1,9 @@
[send-data-formdata.any.html]
[XMLHttpRequest.send(formdata)]
expected: FAIL
[send-data-formdata.any.worker.html]
[XMLHttpRequest.send(formdata)]
expected: FAIL

View file

@ -154,7 +154,7 @@ jobs:
- template: tools/ci/azure/install_certs.yml
- template: tools/ci/azure/update_hosts.yml
- template: tools/ci/azure/update_manifest.yml
- script: python ./wpt run --yes --no-manifest-update --install-fonts --manifest MANIFEST.json --metadata infrastructure/metadata/ --log-tbpl $(Build.ArtifactStagingDirectory)/edge.tbpl.log --log-tbpl-level info edge_webdriver infrastructure/
- script: python ./wpt run --yes --no-manifest-update --install-fonts --manifest MANIFEST.json --metadata infrastructure/metadata/ --webdriver-arg=--verbose --log-tbpl $(Build.ArtifactStagingDirectory)/edge.tbpl.log --log-tbpl-level info edge_webdriver infrastructure/
displayName: 'Run tests (Edge)'
- task: PublishBuildArtifacts@1
displayName: 'Publish results'
@ -183,7 +183,7 @@ jobs:
- template: tools/ci/azure/install_certs.yml
- template: tools/ci/azure/update_hosts.yml
- template: tools/ci/azure/update_manifest.yml
- script: python ./wpt run --no-manifest-update --no-fail-on-unexpected --install-fonts --test-types reftest testharness --this-chunk $(System.JobPositionInPhase) --total-chunks $(System.TotalJobsInPhase) --chunk-type hash --log-tbpl - --log-tbpl-level info --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_$(System.JobPositionInPhase).json edge_webdriver
- script: python ./wpt run --no-manifest-update --no-fail-on-unexpected --install-fonts --webdriver-arg=--verbose --test-types reftest testharness --this-chunk $(System.JobPositionInPhase) --total-chunks $(System.TotalJobsInPhase) --chunk-type hash --log-tbpl - --log-tbpl-level info --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_$(System.JobPositionInPhase).json --log-wptscreenshot $(Build.ArtifactStagingDirectory)/wpt_screenshot_$(System.JobPositionInPhase).txt edge_webdriver
displayName: 'Run tests'
- task: PublishBuildArtifacts@1
displayName: 'Publish results'
@ -217,7 +217,7 @@ jobs:
- template: tools/ci/azure/install_safari.yml
- template: tools/ci/azure/update_hosts.yml
- template: tools/ci/azure/update_manifest.yml
- script: no_proxy='*' ./wpt run --no-manifest-update --no-restart-on-unexpected --no-fail-on-unexpected --this-chunk=$(System.JobPositionInPhase) --total-chunks=$(System.TotalJobsInPhase) --chunk-type hash --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_$(System.JobPositionInPhase).json --channel preview safari
- script: no_proxy='*' ./wpt run --no-manifest-update --no-restart-on-unexpected --no-fail-on-unexpected --this-chunk=$(System.JobPositionInPhase) --total-chunks=$(System.TotalJobsInPhase) --chunk-type hash --log-wptreport $(Build.ArtifactStagingDirectory)/wpt_report_$(System.JobPositionInPhase).json --log-wptscreenshot $(Build.ArtifactStagingDirectory)/wpt_screenshot_$(System.JobPositionInPhase).txt --channel preview safari
displayName: 'Run tests'
- task: PublishBuildArtifacts@1
displayName: 'Publish results'

View file

@ -1,133 +1,51 @@
version: 1
reporting: checks-v1
policy:
pullRequests: public
tasks:
$flattenDeep:
- $if: tasks_for == "github-push"
then:
$map:
$flatten:
$match: {
event.ref == "refs/heads/master": [{name: firefox, channel: nightly}, {name: chrome, channel: dev}],
event.ref == "refs/heads/epochs/daily": [{name: firefox, channel: stable}, {name: chrome, channel: stable}],
event.ref == "refs/heads/epochs/weekly": [{name: firefox, channel: beta}, {name: chrome, channel: beta}]
}
each(browser):
$map:
- [testharness, 1, 15]
- [testharness, 2, 15]
- [testharness, 3, 15]
- [testharness, 4, 15]
- [testharness, 5, 15]
- [testharness, 6, 15]
- [testharness, 7, 15]
- [testharness, 8, 15]
- [testharness, 9, 15]
- [testharness, 10, 15]
- [testharness, 11, 15]
- [testharness, 12, 15]
- [testharness, 13, 15]
- [testharness, 14, 15]
- [testharness, 15, 15]
- [reftest, 1, 10]
- [reftest, 2, 10]
- [reftest, 3, 10]
- [reftest, 4, 10]
- [reftest, 5, 10]
- [reftest, 6, 10]
- [reftest, 7, 10]
- [reftest, 8, 10]
- [reftest, 9, 10]
- [reftest, 10, 10]
- [wdspec, 1, 1]
each(chunk):
taskId: {$eval: 'as_slugid(browser.name + browser.channel + chunk[0] + str(chunk[1]))'}
taskGroupId: {$eval: 'as_slugid("task group")'}
created: {$fromNow: ''}
deadline: {$fromNow: '24 hours'}
provisionerId: aws-provisioner-v1
workerType:
$if: event.repository.full_name == 'web-platform-tests/wpt'
then:
wpt-docker-worker
else:
github-worker
metadata:
name: wpt-${browser.name}-${browser.channel}-${chunk[0]}-${chunk[1]}
description: >-
A subset of WPT's "${chunk[0]}" tests (chunk number ${chunk[1]}
of ${chunk[2]}), run in the ${browser.channel} release of
${browser.name}.
owner: ${event.pusher.email}
source: ${event.repository.url}
payload:
image: harjgam/web-platform-tests:0.29
maxRunTime: 7200
artifacts:
public/results:
path: /home/test/artifacts
type: directory
command:
- /bin/bash
- --login
- -c
- set -ex;
~/start.sh
${event.repository.url}
${event.ref}
${event.after}
${browser.name}
${browser.channel};
cd ~/web-platform-tests;
./tools/ci/taskcluster-run.py
${browser.name}
--
--channel=${browser.channel}
--log-wptreport=../artifacts/wpt_report.json
--log-wptscreenshot=../artifacts/wpt_screenshot.txt
--no-fail-on-unexpected
--test-type=${chunk[0]}
--this-chunk=${chunk[1]}
--total-chunks=${chunk[2]};
- $if: tasks_for == "github-pull-request"
# PR tasks that run the tests in various configurations
then:
# Taskcluster responds to a number of events issued by the GitHub API
# which should not trigger re-validation.
$if: event.action in ['opened', 'reopened', 'synchronize']
$let:
event_str: {$json: {$eval: event}}
in:
$flattenDeep:
- $if: tasks_for == "github-push"
then:
$map: [{name: firefox, channel: nightly}, {name: chrome, channel: dev}]
$map:
$flatten:
$match: {
event.ref == "refs/heads/master": [{name: firefox, channel: nightly}, {name: chrome, channel: dev}],
event.ref == "refs/heads/epochs/daily": [{name: firefox, channel: stable}, {name: chrome, channel: stable}],
event.ref == "refs/heads/epochs/weekly": [{name: firefox, channel: beta}, {name: chrome, channel: beta}]
}
each(browser):
$map:
- name: wpt-${browser.name}-${browser.channel}-stability
checkout: FETCH_HEAD
diff_range: HEAD^
description: >-
Verify that all tests affected by a pull request are stable
when executed in ${browser.name}.
extra_args: '--verify'
- name: wpt-${browser.name}-${browser.channel}-results
checkout: FETCH_HEAD
diff_range: HEAD^
description: >-
Collect results for all tests affected by a pull request in
${browser.name}.
extra_args: >-
--no-fail-on-unexpected
--log-wptreport=../artifacts/wpt_report.json
--log-wptscreenshot=../artifacts/wpt_screenshot.txt
- name: wpt-${browser.name}-${browser.channel}-results-without-changes
checkout: FETCH_HEAD^
diff_range: FETCH_HEAD
description: >-
Collect results for all tests affected by a pull request in
${browser.name} but without the changes in the PR.
extra_args: >-
--no-fail-on-unexpected
--log-wptreport=../artifacts/wpt_report.json
--log-wptscreenshot=../artifacts/wpt_screenshot.txt
each(operation):
taskId: {$eval: 'as_slugid(operation.name)'}
- [testharness, 1, 15]
- [testharness, 2, 15]
- [testharness, 3, 15]
- [testharness, 4, 15]
- [testharness, 5, 15]
- [testharness, 6, 15]
- [testharness, 7, 15]
- [testharness, 8, 15]
- [testharness, 9, 15]
- [testharness, 10, 15]
- [testharness, 11, 15]
- [testharness, 12, 15]
- [testharness, 13, 15]
- [testharness, 14, 15]
- [testharness, 15, 15]
- [reftest, 1, 10]
- [reftest, 2, 10]
- [reftest, 3, 10]
- [reftest, 4, 10]
- [reftest, 5, 10]
- [reftest, 6, 10]
- [reftest, 7, 10]
- [reftest, 8, 10]
- [reftest, 9, 10]
- [reftest, 10, 10]
- [wdspec, 1, 1]
each(chunk):
taskId: {$eval: 'as_slugid(browser.name + browser.channel + chunk[0] + str(chunk[1]))'}
taskGroupId: {$eval: 'as_slugid("task group")'}
created: {$fromNow: ''}
deadline: {$fromNow: '24 hours'}
@ -139,101 +57,273 @@ tasks:
else:
github-worker
metadata:
name: ${operation.name}
description: ${operation.description}
owner: ${event.pull_request.user.login}@users.noreply.github.com
name: wpt-${browser.name}-${browser.channel}-${chunk[0]}-${chunk[1]}
description: >-
A subset of WPT's "${chunk[0]}" tests (chunk number ${chunk[1]}
of ${chunk[2]}), run in the ${browser.channel} release of
${browser.name}.
owner: ${event.pusher.email}
source: ${event.repository.url}
payload:
image: harjgam/web-platform-tests:0.29
image: harjgam/web-platform-tests:0.30
maxRunTime: 7200
artifacts:
public/results:
path: /home/test/artifacts
type: directory
# Fetch the GitHub-provided merge commit (rather than the pull
# request branch) so that the tasks simulate the behavior of the
# submitted patch after it is merged. Using the merge commit also
# simplifies detection of modified files because the first parent
# of the merge commit can consistently be used to summarize the
# changes.
command:
- /bin/bash
- --login
- -c
- set -ex;
echo "wpt-${browser.name}-${browser.channel}-${chunk[0]}-${chunk[1]}";
export TASK_EVENT='${event_str}';
~/start.sh
${event.repository.clone_url}
refs/pull/${event.number}/merge
${operation.checkout}
${browser.name}
${browser.channel};
${event.repository.url}
${event.ref}
${event.after};
cd ~/web-platform-tests;
./tools/ci/taskcluster-run.py
--commit-range ${operation.diff_range}
./tools/ci/run_tc.py
--oom-killer
--hosts
--browser=${browser.name}
--channel=${browser.channel}
--xvfb
./tools/ci/taskcluster-run.py
${browser.name}
--
--channel=${browser.channel}
${operation.extra_args};
- $map:
- name: lint
description: >-
Lint for wpt-specific requirements
script: tools/ci/ci_lint.sh
conditions:
push
pull-request
each(operation):
# Note: jsone doesn't short-circuit evaluation so all parts of the conditional are evaluated
# Accessing properties using the [] notation allows them to evaluate as null in case they're undefined
# TODO: Allow running pushes on branches other than master
- $if: ("push" in operation.conditions && tasks_for == "github-push" && event['ref'] == "refs/heads/master") || ("pull-request" in operation.conditions && tasks_for == "github-pull-request" && event['action'] in ['opened', 'reopened', 'synchronize'])
--log-wptreport=../artifacts/wpt_report.json
--log-wptscreenshot=../artifacts/wpt_screenshot.txt
--no-fail-on-unexpected
--test-type=${chunk[0]}
--this-chunk=${chunk[1]}
--total-chunks=${chunk[2]};
- $if: tasks_for == "github-pull-request"
# PR tasks that run the tests in various configurations
then:
# Taskcluster responds to a number of events issued by the GitHub API
# which should not trigger re-validation.
$if: event.action in ['opened', 'reopened', 'synchronize']
then:
$let:
checkout_ref:
$if: tasks_for == "github-push"
then:
${event.ref}
else:
refs/pull/${event.number}/merge
in:
taskId: {$eval: 'as_slugid(operation.name)'}
taskGroupId: {$eval: 'as_slugid("task group")'}
created: {$fromNow: ''}
deadline: {$fromNow: '24 hours'}
provisionerId: aws-provisioner-v1
workerType:
$if: event.repository.full_name == 'web-platform-tests/wpt'
then:
wpt-docker-worker
else:
github-worker
metadata:
name: ${operation.name}
description: ${operation.description}
owner: ${event.sender.login}@users.noreply.github.com
source: ${event.repository.url}
payload:
image: harjgam/web-platform-tests:0.29
maxRunTime: 7200
artifacts:
public/results:
path: /home/test/artifacts
type: directory
# Fetch the GitHub-provided merge commit (rather than the pull
# request branch) so that the tasks simulate the behavior of the
# submitted patch after it is merged. Using the merge commit also
# simplifies detection of modified files because the first parent
# of the merge commit can consistently be used to summarize the
# changes.
command:
- /bin/bash
- --login
- -c
- set -ex;
~/start.sh
${event.repository.clone_url}
${checkout_ref}
FETCH_HEAD
none;
cd ~/web-platform-tests;
${operation.script};
$map: [{name: firefox, channel: nightly}, {name: chrome, channel: dev}]
each(browser):
$map:
# This is the main place to define new stability checks
- name: wpt-${browser.name}-${browser.channel}-stability
checkout: FETCH_HEAD
diff_range: HEAD^
description: >-
Verify that all tests affected by a pull request are stable
when executed in ${browser.name}.
extra_args: '--verify'
- name: wpt-${browser.name}-${browser.channel}-results
checkout: FETCH_HEAD
diff_range: HEAD^
description: >-
Collect results for all tests affected by a pull request in
${browser.name}.
extra_args: >-
--no-fail-on-unexpected
--log-wptreport=../artifacts/wpt_report.json
--log-wptscreenshot=../artifacts/wpt_screenshot.txt
- name: wpt-${browser.name}-${browser.channel}-results-without-changes
checkout: FETCH_HEAD^
diff_range: FETCH_HEAD
description: >-
Collect results for all tests affected by a pull request in
${browser.name} but without the changes in the PR.
extra_args: >-
--no-fail-on-unexpected
--log-wptreport=../artifacts/wpt_report.json
--log-wptscreenshot=../artifacts/wpt_screenshot.txt
each(operation):
taskId: {$eval: 'as_slugid(operation.name)'}
taskGroupId: {$eval: 'as_slugid("task group")'}
created: {$fromNow: ''}
deadline: {$fromNow: '24 hours'}
provisionerId: aws-provisioner-v1
workerType:
$if: event.repository.full_name == 'web-platform-tests/wpt'
then:
wpt-docker-worker
else:
github-worker
metadata:
name: ${operation.name}
description: ${operation.description}
owner: ${event.pull_request.user.login}@users.noreply.github.com
source: ${event.repository.url}
payload:
image: harjgam/web-platform-tests:0.30
maxRunTime: 7200
artifacts:
public/results:
path: /home/test/artifacts
type: directory
# Fetch the GitHub-provided merge commit (rather than the pull
# request branch) so that the tasks simulate the behavior of the
# submitted patch after it is merged. Using the merge commit also
# simplifies detection of modified files because the first parent
# of the merge commit can consistently be used to summarize the
# changes.
command:
- /bin/bash
- --login
- -c
- set -ex;
echo "${operation.name}";
export TASK_EVENT='${event_str}';
~/start.sh
${event.repository.clone_url}
refs/pull/${event.number}/merge
FETCH_HEAD;
cd web-platform-tests;
./tools/ci/run_tc.py
--checkout=${operation.checkout}
--oom-killer
--browser=${browser.name}
--channel=${browser.channel}
--xvfb
stability
./tools/ci/taskcluster-run.py
--commit-range ${operation.diff_range}
${browser.name}
--
--channel=${browser.channel}
${operation.extra_args};
- $map:
# This is the main point to define new CI checks other than stability checks
- name: lint
description: >-
Lint for wpt-specific requirements
script: ./tools/ci/run_tc.py --no-hosts lint tools/ci/ci_lint.sh
conditions:
push
pull-request
- name: update built tests
description: >-
Ensure test suites that require a build step are updated
script: ./tools/ci/run_tc.py --no-hosts update_built tools/ci/ci_built_diff.sh
conditions:
pull-request
- name: tools/ unittests (Python 2)
description: >-
Unit tests for tools running under Python 2.7, excluding wptrunner
script: >-
export TOXENV=py27;
export HYPOTHESIS_PROFILE=ci;
./tools/ci/run_tc.py \
tools_unittest \
tools/ci/ci_tools_unittest.sh
conditions:
push
pull-request
- name: tools/ unittests (Python 3)
description: >-
Unit tests for tools running under Python 3, excluding wptrunner
script: >-
export TOXENV=py36;
export HYPOTHESIS_PROFILE=ci;
sudo apt install -qqy python3-pip;
./tools/ci/run_tc.py \
tools_unittest \
tools/ci/ci_tools_unittest.sh
conditions:
push
pull-request
- name: tools/wpt/ tests
description: >-
Integration tests for wpt commands
script: >-
export TOXENV=py27;
sudo apt install -qqy libnss3-tools;
./tools/ci/run_tc.py \
--oom-killer \
--browser=firefox \
--browser=chrome \
--channel=experimental \
--xvfb \
wpt_integration \
tools/ci/ci_wpt.sh
conditions:
pull-request
- name: resources/ tests
description: >-
Tests for testharness.js and other files in resources/
script: >-
export TOXENV=py27;
./tools/ci/run_tc.py \
--browser=firefox \
--channel=experimental \
--xvfb \
resources_unittest \
tools/ci/ci_resources_unittest.sh
conditions:
pull-request
- name: infrastructure/ tests
description: >-
Smoketests for wptrunner
script: >-
sudo apt install -qqy libnss3-tools libappindicator1 fonts-liberation;
./tools/ci/run_tc.py \
--oom-killer \
--browser=firefox \
--browser=chrome \
--channel=experimental \
--no-hosts \
--xvfb \
wptrunner_infrastructure \
tools/ci/ci_wptrunner_infrastructure.sh
conditions:
pull-request
each(operation):
# Note: jsone doesn't short-circuit evaluation so all parts of the conditional are evaluated
# Accessing properties using the [] notation allows them to evaluate as null in case they're undefined
# TODO: Allow running pushes on branches other than master
- $if: ("push" in operation.conditions && tasks_for == "github-push" && event['ref'] == "refs/heads/master") || ("pull-request" in operation.conditions && tasks_for == "github-pull-request" && event['action'] in ['opened', 'reopened', 'synchronize'])
then:
$let:
checkout_ref:
$if: tasks_for == "github-push"
then:
${event.ref}
else:
refs/pull/${event.number}/merge
in:
taskId: {$eval: 'as_slugid(operation.name)'}
taskGroupId: {$eval: 'as_slugid("task group")'}
created: {$fromNow: ''}
deadline: {$fromNow: '24 hours'}
provisionerId: aws-provisioner-v1
workerType:
$if: event.repository.full_name == 'web-platform-tests/wpt'
then:
wpt-docker-worker
else:
github-worker
metadata:
name: ${operation.name}
description: ${operation.description}
owner: ${event.sender.login}@users.noreply.github.com
source: ${event.repository.url}
payload:
image: harjgam/web-platform-tests:0.30
maxRunTime: 7200
artifacts:
public/results:
path: /home/test/artifacts
type: directory
command:
- /bin/bash
- --login
- -c
- set -ex;
echo "${operation.name}";
export TASK_EVENT='${event_str}';
~/start.sh
${event.repository.clone_url}
${checkout_ref}
FETCH_HEAD;
cd ~/web-platform-tests;
${operation.script};

View file

@ -28,66 +28,11 @@ matrix:
secure: "EljDx50oNpDLs7rzwIv+z1PxIgB5KMnx1W0OQkpNvltR0rBW9g/aQaE+Z/c8M/sPqN1bkvKPybKzGKjb6j9Dw3/EJhah4SskH78r3yMAe2DU/ngxqqjjfXcCc2t5MKxzHAILTAxqScPj2z+lG1jeK1Z+K5hTbSP9lk+AvS0D16w="
file: $WPT_MANIFEST_FILE.gz
skip_cleanup: true
- name: "update-built-tests.sh"
if: type = pull_request
os: linux
python: "2.7"
env: JOB=update_built SCRIPT=tools/ci/ci_built_diff.sh
- name: "build-css-testsuites.sh"
if: type = pull_request
os: linux
python: "2.7"
env: JOB=build_css SCRIPT=css/build-css-testsuites.sh
- name: "tools/ unittests (Python 2)"
if: type = pull_request
os: linux
python: "2.7"
env: JOB=tools_unittest TOXENV=py27 HYPOTHESIS_PROFILE=ci SCRIPT=tools/ci/ci_tools_unittest.sh
- name: "tools/ unittests (Python 3)"
if: type = pull_request
os: linux
python: "3.6"
env: JOB=tools_unittest TOXENV=py36 HYPOTHESIS_PROFILE=ci SCRIPT=tools/ci/ci_tools_unittest.sh
- name: "tools/wpt/ tests"
if: type = pull_request
os: linux
python: "2.7"
addons:
apt:
packages:
- libnss3-tools
env: JOB=wpt_integration TOXENV=py27 SCRIPT=tools/ci/ci_wpt.sh
addons:
apt:
packages:
- fonts-liberation
- libappindicator1
- libnss3-tools
- pulseaudio
- name: "resources/ tests"
if: type = pull_request
os: linux
python: "2.7"
env: JOB=resources_unittest TOXENV=py27 SCRIPT=tools/ci/ci_resources_unittest.sh
addons:
apt:
packages:
- fonts-liberation
- libappindicator1
- libnss3-tools
- pulseaudio
- name: "infrastructure/ tests"
if: type = pull_request
os: linux
python: "2.7"
env: JOB=wptrunner_infrastructure SCRIPT=tools/ci/ci_wptrunner_infrastructure.sh
addons:
apt:
packages:
- fonts-liberation
- libappindicator1
- libnss3-tools
- pulseaudio
exclude:
- env: # exclude empty env from the top-level above
allow_failures:

View file

@ -0,0 +1,40 @@
// META: script=support.js
function cursorRequestTest({ useIndex, useKeyCursor }) {
indexeddb_test(
(t, db) => {
const objStore = db.createObjectStore("my_objectstore");
objStore.add("data", 1);
objStore.createIndex("my_index", "");
},
(t, db) => {
const tx = db.transaction("my_objectstore");
let source = tx.objectStore("my_objectstore");
if (useIndex) source = source.index('my_index');
const req = useKeyCursor ? source.openKeyCursor() : source.openCursor();
let cursor;
req.onsuccess = t.step_func(() => {
cursor = req.result;
assert_equals(cursor.request, req, 'cursor.request');
assert_readonly(cursor, 'request');
});
req.transaction.oncomplete = t.step_func(() => {
setTimeout(t.step_func(() => {
assert_equals(cursor.request, req, 'cursor.request after transaction complete');
t.done();
}), 0);
});
req.transaction.onerror = t.unreached_func('Transaction error');
},
`cursor.request from ${useIndex ? 'IDBIndex' : 'IDBObjectStore'}.${useKeyCursor ? 'openKeyCursor' : 'openCursor'}`
);
}
for (const useIndex of [false, true]) {
for (const useKeyCursor of [false, true]) {
cursorRequestTest({ useIndex, useKeyCursor });
}
}

View file

@ -0,0 +1,41 @@
<!DOCTYPE html>
<title>Databases on different origins use separate locking</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/common/get-host-info.sub.js"></script>
<script src="support.js"></script>
<script src="support-promises.js"></script>
<script>
var host_info = get_host_info();
promise_test(async testCase => {
await deleteAllDatabases(testCase);
// Create an iframe to open and hold a database on a different origin.
var iframe = document.createElement('iframe');
var newLocation = window.location.href.replace('www', 'www2');
const keepalive_watcher = new EventWatcher(testCase, window, 'message');
iframe.src = host_info.HTTP_REMOTE_ORIGIN +
'/IndexedDB/resources/idbfactory-origin-isolation-iframe.html';
document.body.appendChild(iframe);
// Wait until the iframe starts its transaction.
var event = await keepalive_watcher.wait_for('message');
assert_equals("keep_alive_started", event.data);
// Create our own database with the same name, and perform a simple get.
const db = await createNamedDatabase(
testCase, 'db-isolation-test', database => {
database.createObjectStore('s');
});
const tx = db.transaction('s');
var request = tx.objectStore('s').get(0);
request.onsuccess = testCase.step_func_done();
request.onerror = testCase.unreached_func("There should be no errors.");
}, "Test to make sure that origins have separate locking schemes");
</script>
<div id="log"></div>

View file

@ -0,0 +1,50 @@
<!DOCTYPE html>
<title>This iframe keeps a transaction on a database alive indefinitely to test</title>
<script>
// Keeps the passed transaction alive indefinitely (by making requests
// against the named store). Returns a function that asserts that the
// transaction has not already completed and then ends the request loop so that
// the transaction may autocommit and complete.
function keep_alive(tx, store_name) {
let completed = false;
tx.addEventListener('complete', () => { completed = true; });
let keepSpinning = true;
function spin() {
if (!keepSpinning)
return;
tx.objectStore(store_name).get(0).onsuccess = spin;
}
spin();
return () => {
assert_false(completed, 'Transaction completed while kept alive');
keepSpinning = false;
};
}
async function run() {
const dbs_to_delete = await indexedDB.databases();
for (const db_info of dbs_to_delete) {
let request = indexedDB.deleteDatabase(db_info.name);
await new Promise((resolve, reject) => {
request.onsuccess = resolve;
request.onerror = reject;
});
}
var openRequest = indexedDB.open('db-isolation-test');
openRequest.onupgradeneeded = () => {
openRequest.result.createObjectStore('s');
};
openRequest.onsuccess = () => {
var tx = openRequest.result.transaction('s');
keep_alive(tx, 's');
window.parent.postMessage("keep_alive_started", "*");
};
}
run();
</script>

View file

@ -48,7 +48,7 @@ async function waitForAnimationFrameWithCondition(condition) {
do {
await new Promise(window.requestAnimationFrame);
} while (!condition())
};
}
async function waitForDocumentTimelineAdvance() {
const timeAtStart = document.timeline.currentTime;
@ -56,3 +56,10 @@ async function waitForDocumentTimelineAdvance() {
await new Promise(window.requestAnimationFrame);
} while (timeAtStart === document.timeline.currentTime)
}
// Wait until animation's effect has a non-null localTime.
async function waitForNotNullLocalTime(animation) {
await waitForAnimationFrameWithCondition(_ => {
return animation.effect.getComputedTiming().localTime !== null;
});
}

View file

@ -50,110 +50,98 @@ function setupAndRegisterTests() {
}
async function effect_with_fill_mode_forwards(t) {
const effect_with_fill_forwards = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 1000, fill: 'forwards' });
const animation = new WorkletAnimation(
'constant_time',
effect_with_fill_forwards);
animation.play();
const effect_with_fill_forwards = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 1000, fill: 'forwards' });
const animation = new WorkletAnimation(
'constant_time',
effect_with_fill_forwards);
animation.play();
await waitForNotNullLocalTime(animation);
await waitForAsyncAnimationFrames(1);
await waitForNextFrame();
assert_equals(getComputedStyle(target).opacity, '0');
assert_equals(getComputedStyle(target).opacity, '0');
animation.cancel();
animation.cancel();
}
async function effect_without_fill_mode_forwards(t) {
const effect_without_fill_forwards = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 1000 });
const animation = new WorkletAnimation(
'constant_time',
effect_without_fill_forwards);
animation.play();
const effect_without_fill_forwards = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 1000 });
const animation = new WorkletAnimation(
'constant_time',
effect_without_fill_forwards);
animation.play();
await waitForNotNullLocalTime(animation);
await waitForAsyncAnimationFrames(1);
await waitForNextFrame();
assert_equals(getComputedStyle(target).opacity, '1');
assert_equals(getComputedStyle(target).opacity, '1');
animation.cancel();
animation.cancel();
}
async function effect_without_fill_forwards_at_end(t) {
const effect_without_fill_forwards_at_end = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 2000 });
const animation = new WorkletAnimation(
'constant_time',
effect_without_fill_forwards_at_end);
animation.play();
const effect_without_fill_forwards_at_end = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 2000 });
const animation = new WorkletAnimation(
'constant_time',
effect_without_fill_forwards_at_end);
animation.play();
await waitForNotNullLocalTime(animation);
await waitForAsyncAnimationFrames(1);
await waitForNextFrame();
assert_equals(getComputedStyle(target).opacity, '1');
assert_equals(getComputedStyle(target).opacity, '1');
animation.cancel();
animation.cancel();
}
async function effect_with_fill_backwards(t) {
const effect_with_fill_backwards = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 1000, delay: 2001, fill: 'backwards' });
const animation = new WorkletAnimation(
'constant_time',
effect_with_fill_backwards);
animation.play();
const effect_with_fill_backwards = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 1000, delay: 2001, fill: 'backwards' });
const animation = new WorkletAnimation(
'constant_time',
effect_with_fill_backwards);
animation.play();
await waitForNotNullLocalTime(animation);
await waitForAsyncAnimationFrames(1);
await waitForNextFrame();
assert_equals(getComputedStyle(target).opacity, '0.5');
assert_equals(getComputedStyle(target).opacity, '0.5');
animation.cancel();
animation.cancel();
}
async function effect_without_fill_backwards(t) {
const effect_without_fill_backwards = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 1000, delay: 2001 });
const animation = new WorkletAnimation(
'constant_time',
effect_without_fill_backwards);
animation.play();
const effect_without_fill_backwards = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 1000, delay: 2001 });
const animation = new WorkletAnimation(
'constant_time',
effect_without_fill_backwards);
animation.play();
waitForNotNullLocalTime(animation);
await waitForAsyncAnimationFrames(1);
await waitForNextFrame();
assert_equals(getComputedStyle(target).opacity, '1');
assert_equals(getComputedStyle(target).opacity, '1');
animation.cancel();
animation.cancel();
}
async function effect_without_fill_backwards_at_start(t) {
const effect_without_fill_backwards_at_start = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 1000, delay: 2000 });
const animation = new WorkletAnimation(
'constant_time',
effect_without_fill_backwards_at_start);
animation.play();
const effect_without_fill_backwards_at_start = new KeyframeEffect(
target,
{ opacity: [0.5, 0] },
{ duration: 1000, delay: 2000 });
const animation = new WorkletAnimation(
'constant_time',
effect_without_fill_backwards_at_start);
animation.play();
await waitForNotNullLocalTime(animation);
await waitForAsyncAnimationFrames(1);
await waitForNextFrame();
assert_equals(getComputedStyle(target).opacity, '0.5');
assert_equals(getComputedStyle(target).opacity, '0.5');
animation.cancel();
animation.cancel();
}
</script>

View file

@ -1,11 +1,11 @@
<!DOCTYPE html>
<meta charset=utf-8>
<title>Clipboard IDL test</title>
<link rel="help" href="https://w3c.github.io/clipboard-apis/">
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src=/resources/WebIDLParser.js></script>
<script src=/resources/idlharness.js></script>
<link rel='help' href='https://w3c.github.io/clipboard-apis/'>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/WebIDLParser.js"></script>
<script src="/resources/idlharness.js"></script>
<script>
'use strict';
@ -31,9 +31,9 @@ function fetchText(url) {
promise_test(() => {
return Promise.all(
[
"/interfaces/clipboard-apis.idl",
"/interfaces/dom.idl",
'/interfaces/clipboard-apis.idl',
'/interfaces/dom.idl',
].map(fetchText))
.then(([idl, dom]) => doTest(idl, dom));
}, "Test driver");
}, 'Test driver');
</script>

View file

@ -9,55 +9,59 @@ test(() => {
assert_not_equals(navigator.clipboard, undefined);
assert_true(navigator.clipboard instanceof Clipboard);
assert_equals(navigator.clipboard, navigator.clipboard);
}, "navigator.clipboard exists");
}, 'navigator.clipboard exists');
promise_test(async () => {
const blob = new Blob(["hello"], {type: 'text/plain'});
await navigator.clipboard.write([blob]);
}, "navigator.clipboard.write([text/plain Blob]) succeeds");
const blob = new Blob(['hello'], {type: 'text/plain'});
await navigator.clipboard.write({'text/plain': blob});
}, 'navigator.clipboard.write({string : text/plain Blob}) succeeds');
promise_test(async t => {
await promise_rejects(t, new TypeError(),
navigator.clipboard.write());
}, "navigator.clipboard.write() fails (expect [Blob])");
}, 'navigator.clipboard.write() fails (expect {string : Blob})');
promise_test(async t => {
await promise_rejects(t, new TypeError(),
navigator.clipboard.write(null));
}, "navigator.clipboard.write(null) fails (expect [Blob])");
}, 'navigator.clipboard.write(null) fails (expect {string : Blob})');
promise_test(async t => {
await promise_rejects(t, new TypeError(),
navigator.clipboard.write("Bad string"));
}, "navigator.clipboard.write(DOMString) fails (expect [Blob])");
navigator.clipboard.write('Bad string'));
}, 'navigator.clipboard.write(DOMString) fails (expect {string : Blob})');
promise_test(async t => {
const blob = new Blob(['hello'], {type: 'text/plain'});
await promise_rejects(t, 'NotAllowedError',
navigator.clipboard.write(blob));
}, 'navigator.clipboard.write(Blob) fails (expect {string : Blob})');
promise_test(async () => {
await navigator.clipboard.writeText("New clipboard text");
}, "navigator.clipboard.writeText(DOMString) succeeds");
await navigator.clipboard.writeText('New clipboard text');
}, 'navigator.clipboard.writeText(DOMString) succeeds');
promise_test(async t => {
await promise_rejects(t, new TypeError(),
navigator.clipboard.writeText());
}, "navigator.clipboard.writeText() fails (expect DOMString)");
}, 'navigator.clipboard.writeText() fails (expect DOMString)');
promise_test(async () => {
const fetched = await fetch(
'http://localhost:8001/clipboard-apis/resources/greenbox.png');
const image = await fetched.blob();
await navigator.clipboard.write([image]);
}, "navigator.clipboard.write([image/png Blob]) succeeds");
await navigator.clipboard.write({'image/png' : image});
}, 'navigator.clipboard.write({string : image/png Blob}) succeeds');
promise_test(async () => {
const result = await navigator.clipboard.read();
assert_true(result instanceof Array);
assert_true(result[0] instanceof Blob);
assert_equals(typeof result, "object");
}, "navigator.clipboard.read() succeeds");
assert_true(result instanceof Object);
}, 'navigator.clipboard.read() succeeds');
promise_test(async () => {
const result = await navigator.clipboard.readText();
assert_equals(typeof result, "string");
}, "navigator.clipboard.readText() succeeds");
assert_equals(typeof result, 'string');
}, 'navigator.clipboard.readText() succeeds');
</script>

View file

@ -13,19 +13,20 @@ async function loadBlob(fileName) {
}
promise_test(async t => {
const blobText = new Blob(["test text"], {type: 'text/plain'});
const blobText = new Blob(['test text'], {type: 'text/plain'});
const blobImage = await loadBlob('resources/greenbox.png');
assert_equals(blobText.type, "text/plain");
assert_equals(blobImage.type, "image/png");
assert_equals(blobText.type, 'text/plain');
assert_equals(blobImage.type, 'image/png');
await navigator.clipboard.write([blobText, blobImage]);
await navigator.clipboard.write(
{'text/plain' : blobText, 'image/png' : blobImage});
const output = await navigator.clipboard.read();
assert_equals(output.length, 2);
assert_equals(output[0].type, "text/plain");
assert_equals(output[1].type, "image/png");
}, "Verify write and read clipboard (multiple blobs)");
assert_equals(Object.keys(output).length, 2);
assert_equals(output['text/plain'].type, 'text/plain');
assert_equals(output['image/png'].type, 'image/png');
}, 'Verify write and read clipboard (multiple blobs)');
</script>
<p>
Note: This is a manual test because it writes/reads to the shared system

View file

@ -10,19 +10,19 @@ async function readWriteTest(textInput) {
promise_test(async t => {
const blobInput = new Blob([textInput], {type: 'text/plain'});
await navigator.clipboard.write([blobInput]);
await navigator.clipboard.write({'text/plain': blobInput});
const blobsOutput = await navigator.clipboard.read();
assert_equals(blobsOutput.length, 1);
const blobOutput = blobsOutput[0];
assert_equals(blobOutput.type, "text/plain");
assert_equals(Object.keys(blobsOutput).length, 1);
const blobOutput = blobsOutput['text/plain'];
assert_equals(blobOutput.type, 'text/plain');
const textOutput = await (new Response(blobOutput)).text();
assert_equals(textOutput, textInput);
}, "Verify write and read clipboard given text: " + textInput);
}, 'Verify write and read clipboard given text: ' + textInput);
}
readWriteTest("Clipboard write ([text/plain Blob]) -> read ([text/plain Blob]) test");
readWriteTest("non-Latin1 text encoding test データ");
readWriteTest('Clipboard write ([text/plain Blob]) -> read ([text/plain Blob]) test');
readWriteTest('non-Latin1 text encoding test データ');
</script>
<p>
Note: This is a manual test because it writes/reads to the shared system

View file

@ -8,15 +8,15 @@ async function readWriteTest(textInput) {
promise_test(async t => {
const blobInput = new Blob([textInput], {type: 'text/plain'});
await navigator.clipboard.write([blobInput]);
await navigator.clipboard.write({'text/plain': blobInput});
const textOutput = await navigator.clipboard.readText();
assert_equals(textOutput, textInput);
}, "Verify write and read clipboard given text: " + textInput);
}, 'Verify write and read clipboard given text: ' + textInput);
}
readWriteTest("Clipboard write ([text/plain Blob]) -> read text test");
readWriteTest("non-Latin1 text encoding test データ");
readWriteTest('Clipboard write ([text/plain Blob]) -> read text test');
readWriteTest('non-Latin1 text encoding test データ');
</script>
<p>
Note: This is a manual test because it writes/reads to the shared system

View file

@ -1,26 +0,0 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>
Async Clipboard write duplicate mime type test
</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
promise_test(async t => {
const blobText = new Blob(["test text"], {type: 'text/plain'});
const blobText2 = new Blob(["test text"], {type: 'text/plain'});
assert_equals(blobText.type, "text/plain");
assert_equals(blobText2.type, "text/plain");
await promise_rejects(t, 'NotAllowedError',
navigator.clipboard.write([blobText, blobText2]));
}, "Verify write and read clipboard (multiple blobs)");
</script>
<p>
Note: This is a manual test because it writes/reads to the shared system
clipboard and thus cannot be run async with other tests that might interact
with the clipboard.
</p>

View file

@ -9,11 +9,11 @@
<p>
<p>The bottom image should display the same image as the top image.</p>
<p>Original Image:</p>
<image id='image-to-copy' width='20' height='20'
<image id="image-to-copy" width="20" height="20"
src="resources/greenbox.png"></image>
<p>Image after copy/paste:</p>
<image id='image-on-clipboard'></image>
<canvas id='canvas' width='20' height='20'></canvas>
<image id="image-on-clipboard"></image>
<canvas id="canvas" width="20" height="20"></canvas>
</p>
<script>
@ -39,12 +39,12 @@ async function loadBlob(fileName) {
promise_test(async t => {
const blobInput = await loadBlob('resources/greenbox.png');
assert_equals(blobInput.type, "image/png");
await navigator.clipboard.write([blobInput]);
assert_equals(blobInput.type, 'image/png');
await navigator.clipboard.write({'image/png' : blobInput});
const blobsOutput = await navigator.clipboard.read();
assert_equals(blobsOutput.length, 1);
const blobOutput = blobsOutput[0];
assert_equals(blobOutput.type, "image/png");
assert_equals(Object.keys(blobsOutput).length, 1);
const blobOutput = blobsOutput['image/png'];
assert_equals(blobOutput.type, 'image/png');
document.getElementById('image-on-clipboard').src =
window.URL.createObjectURL(blobOutput);
@ -53,7 +53,7 @@ promise_test(async t => {
const comparableOutput = await getBitmapString(blobOutput);
assert_equals(comparableOutput, comparableInput);
}, "Verify write and read clipboard ([image/png Blob])");
}, 'Verify write and read clipboard ([image/png Blob])');
</script>
<p>
Note: This is a manual test because it writes/reads to the shared system

View file

@ -8,17 +8,17 @@ async function readWriteTest(textInput) {
promise_test(async t => {
await navigator.clipboard.writeText(textInput);
const blobsOutput = await navigator.clipboard.read();
assert_equals(blobsOutput.length, 1);
const blobOutput = blobsOutput[0];
assert_equals(blobOutput.type, "text/plain");
assert_equals(Object.keys(blobsOutput).length, 1);
const blobOutput = blobsOutput['text/plain'];
assert_equals(blobOutput.type, 'text/plain');
const textOutput = await (new Response(blobOutput)).text();
assert_equals(textOutput, textInput);
}, "Verify write and read clipboard given text: " + textInput);
}, 'Verify write and read clipboard given text: ' + textInput);
}
readWriteTest("Clipboard write text -> read ([text/plain Blob]) test");
readWriteTest("non-Latin1 text encoding test データ");
readWriteTest('Clipboard write text -> read ([text/plain Blob]) test');
readWriteTest('non-Latin1 text encoding test データ');
</script>
<p>
Note: This is a manual test because it writes/reads to the shared system

View file

@ -10,11 +10,11 @@ async function readWriteTest(textInput) {
const textOutput = await navigator.clipboard.readText();
assert_equals(textOutput, textInput);
}, "Verify write and read clipboard given text: " + textInput);
}, 'Verify write and read clipboard given text: ' + textInput);
}
readWriteTest("Clipboard write text -> read text test");
readWriteTest("non-Latin1 text encoding test データ");
readWriteTest('Clipboard write text -> read text test');
readWriteTest('non-Latin1 text encoding test データ');
</script>
<p>
Note: This is a manual test because it writes/reads to the shared system

View file

@ -11,9 +11,9 @@ async_test(t => {
document.oncopy = t.step_func_done(event => {
// Nothing can be asserted about the event target until
// https://github.com/w3c/clipboard-apis/issues/70 is resolved.
// assert_equals(event.target, document.body, "event.target");
assert_true(event.isTrusted, "event.isTrusted");
assert_true(event.composed, "event.composed");
// assert_equals(event.target, document.body, 'event.target');
assert_true(event.isTrusted, 'event.isTrusted');
assert_true(event.composed, 'event.composed');
});
});
</script>

View file

@ -11,9 +11,9 @@ async_test(t => {
document.oncut = t.step_func_done(event => {
// Nothing can be asserted about the event target until
// https://github.com/w3c/clipboard-apis/issues/70 is resolved.
// assert_equals(event.target, document.body, "event.target");
assert_true(event.isTrusted, "event.isTrusted");
assert_true(event.composed, "event.composed");
// assert_equals(event.target, document.body, 'event.target');
assert_true(event.isTrusted, 'event.isTrusted');
assert_true(event.composed, 'event.composed');
});
});
</script>

View file

@ -9,13 +9,13 @@
<script>
setup({explicit_timeout: true});
async_test(t => {
getSelection().selectAllChildren(document.querySelector("p"));
getSelection().selectAllChildren(document.querySelector('p'));
document.onpaste = t.step_func_done(event => {
// Nothing can be asserted about the event target until
// https://github.com/w3c/clipboard-apis/issues/70 is resolved.
// assert_equals(event.target, document.body, "event.target");
assert_true(event.isTrusted, "event.isTrusted");
assert_true(event.composed, "event.composed");
// assert_equals(event.target, document.body, 'event.target');
assert_true(event.isTrusted, 'event.isTrusted');
assert_true(event.composed, 'event.composed');
});
});
</script>

View file

@ -1,21 +0,0 @@
<!DOCTYPE html>
<link rel="author" title="Google" href="https://www.google.com/" />
<link rel="help" href="https://drafts.csswg.org/css-flexbox-1/#intrinsic-sizes" />
<link rel="help" href="https://github.com/w3c/csswg-drafts/issues/1865" />
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<body onload="checkLayout('.flexbox')">
<div class="flexbox" style="display: flex; width: min-content;" data-expected-width="0">
<div style="overflow: auto;">
<div style="width: 100px; height: 100px;"></div>
</div>
</div>
<div class="flexbox" style="display: flex; width: min-content;" data-expected-width="10">
<div style="overflow: auto; border: 5px solid;">
<div style="width: 100px; height: 100px;"></div>
</div>
</div>

View file

@ -0,0 +1,153 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>CSS Grid Layout Test: Grid container baseline</title>
<link rel="author" title="Manuel Rego Casasnovas" href="mailto:rego@igalia.com">
<link rel="help" href="https://drafts.csswg.org/css-grid-1/#grid-baselines">
<meta name="assert" content="Grid container baseline should match the element with 'align-self: baseline' in the first row, even if it's an orthogonal element.">
<style>
.wrapper {
position: relative;
margin: 10px;
}
.grid {
display: inline-grid;
grid-auto-flow: column;
background: grey;
}
.i1 {
width: 150px;
height: 100px;
background: magenta;
}
.i2 {
align-self: baseline;
width: 75px;
height: 50px;
background: cyan;
}
.i3 {
width: 100px;
height: 75px;
background: yellow;
}
</style>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/check-layout-th.js"></script>
<body onload="checkLayout('.wrapper')">
<div class="wrapper" style="writing-mode: horizontal-tb;">
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-y="40"></div>
<div class="grid">
<div class="i1"></div>
<div class="i2" data-offset-y="0"></div>
<div class="i3"></div>
</div>
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-y="40"></div>
</div>
<div class="wrapper" style="writing-mode: horizontal-tb;">
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-y="40"></div>
<div class="grid">
<div class="i1"></div>
<div class="i2" style="writing-mode: vertical-lr;" data-offset-y="0"></div>
<div class="i3"></div>
</div>
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-y="40"></div>
</div>
<div class="wrapper" style="writing-mode: horizontal-tb;">
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-y="40"></div>
<div class="grid">
<div class="i1"></div>
<div class="i2" style="writing-mode: vertical-rl;" data-offset-y="0"></div>
<div class="i3"></div>
</div>
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-y="40"></div>
</div>
<div class="wrapper" style="writing-mode: vertical-lr;">
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="65"></div>
<div class="grid">
<div class="i1"></div>
<div class="i2" data-offset-x="0"></div>
<div class="i3"></div>
</div>
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="65"></div>
</div>
<div class="wrapper" style="writing-mode: vertical-lr;">
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="65"></div>
<div class="grid">
<div class="i1"></div>
<div class="i2" style="writing-mode: horizontal-tb;" data-offset-x="0"></div>
<div class="i3"></div>
</div>
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="65"></div>
</div>
<div class="wrapper" style="writing-mode: vertical-lr; text-orientation: sideways;">
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="75"></div>
<div class="grid">
<div class="i1"></div>
<div class="i2" data-offset-x="0"></div>
<div class="i3"></div>
</div>
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="75"></div>
</div>
<div class="wrapper" style="writing-mode: vertical-lr; text-orientation: sideways;">
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="75"></div>
<div class="grid">
<div class="i1"></div>
<div class="i2" style="writing-mode: horizontal-tb;" data-offset-x="0"></div>
<div class="i3"></div>
</div>
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="75"></div>
</div>
<div class="wrapper" style="writing-mode: vertical-rl;">
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="65"></div>
<div class="grid">
<div class="i1"></div>
<div class="i2" data-offset-x="75"></div>
<div class="i3"></div>
</div>
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="65"></div>
</div>
<div class="wrapper" style="writing-mode: vertical-rl;">
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="65"></div>
<div class="grid">
<div class="i1"></div>
<div class="i2" style="writing-mode: horizontal-tb;" data-offset-x="75"></div>
<div class="i3"></div>
</div>
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="65"></div>
</div>
<div class="wrapper" style="writing-mode: vertical-rl; text-orientation: sideways;">
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="75"></div>
<div class="grid">
<div class="i1"></div>
<div class="i2" data-offset-x="75"></div>
<div class="i3"></div>
</div>
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="75"></div>
</div>
<div class="wrapper" style="writing-mode: vertical-rl; text-orientation: sideways;">
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="75"></div>
<div class="grid">
<div class="i1"></div>
<div class="i2" style="writing-mode: horizontal-tb;" data-offset-x="75"></div>
<div class="i3"></div>
</div>
<div style="display: inline-block; width: 20px; height: 10px; background: black" data-offset-x="75"></div>
</div>
</body>

View file

@ -1,5 +1,5 @@
<!DOCTYPE html>
<title>CSS Position Absolute: Chrome chrash</title>
<title>CSS Position Absolute: Chrome crash</title>
<link rel="author" href="mailto:atotic@google.com">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

View file

@ -0,0 +1,29 @@
<!DOCTYPE html>
<title>CSS Position Absolute: Chrome crash</title>
<link rel="author" href="mailto:atotic@chromium.org">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<link rel="help" href="https://bugs.chromium.org/p/chromium/issues/detail?id=935805">
<meta name="assert" content="Nested abs/fixed/flex do not crash">
<style>
#flex {
display: flex;
}
.abs {
position: absolute;
}
#fixed {
position: fixed;
}
</style>
<div class="abs">
<div id="flex">
<div class="abs">
<div id="fixed"></div>
</div>
</div>
</div>
<script>
test(() => {
}, 'test passes if it does not crash');
</script>

View file

@ -0,0 +1,25 @@
<!doctype html>
<html>
<head>
<title>Absolute inside inline container location should be correct</title>
<link rel="author" href="mailto:atotic@chromium.org">
<link rel="help" href="https://www.w3.org/TR/css-position-3/#def-cb">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
</head>
<!-- There should be a green square below -->
<body style="margin:0">
<span id="container" style="position:relative;">
<div style="width:100px; height:100px; background:red;"></div>
<div id="target" style="position:absolute; left:0; top:0; width:100px; height:100px; background:green;"></div>
</span>
<script>
test(_ => {
let bounds = document.querySelector("#target").getBoundingClientRect();
let container_bounds = document.querySelector("#container").getBoundingClientRect();
assert_equals(bounds.x, container_bounds.x);
assert_equals(bounds.y, container_bounds.y);
}, "absolute inside inline container location should be correct.");
</script>
</body>
</html>

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html class="reftest-wait">
<meta charset="utf-8">
<title>position:sticky should operate correctly</title>
<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
<div style="position: fixed;">There should be text visible below.</div>
<div style="height: 200px;width:600px;position: fixed;top: 0px;">
<div style="position: relative;top: 100px;">
<div style="height: 150px;width: 500px;position: absolute;backface-visibility: hidden;background: white;">
</div>
<div style="overflow: hidden;">
<a style="position: relative;">THIS SHOULD STAY VISIBLE<BR>IF YOU SCROLL DOWN</a>
</div>
</div>
</div>
<div style="height: 2200px;">
</div>
<script>
window.onload = function() {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
window.scrollTo(0,300);
document.documentElement.classList.remove("reftest-wait");
});
});
};
</script>
</html>

View file

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html class="reftest-wait">
<meta charset="utf-8">
<title>position:sticky should operate correctly</title>
<link rel="author" title="Mason Freed" href="mailto:masonfreed@chromium.org">
<link rel="help" href="https://www.w3.org/TR/css-position-3/#sticky-pos" />
<meta name="assert" content="This test checks that the combination of position:sticky, overflow clip, and out-of-flow descendants are properly displayed when scrolled" />
<link rel="match" href="position-sticky-scroll-with-clip-and-abspos-ref.html">
<div style="position: fixed;">There should be text visible below.</div>
<div style="height: 200px;width:600px;position: sticky;top: 0px;">
<div style="position: relative;top: 100px;">
<div style="height: 150px;width: 500px;position: absolute;backface-visibility: hidden;background: white;">
</div>
<div style="overflow: hidden;">
<a style="position: relative;">THIS SHOULD STAY VISIBLE<BR>IF YOU SCROLL DOWN</a>
</div>
</div>
</div>
<div style="height: 2000px;">
</div>
<script>
window.onload = function() {
requestAnimationFrame(() => {
requestAnimationFrame(() => {
window.scrollTo(0,300);
document.documentElement.classList.remove("reftest-wait");
});
});
};
</script>
</html>

View file

@ -0,0 +1,34 @@
<!DOCTYPE html>
<script src='/resources/testharness.js'></script>
<script src='/resources/testharnessreport.js'></script>
<link rel="author" title="David Grogan" href="dgrogan@chromium.org">
<link rel="help" href="https://www.w3.org/TR/css-writing-modes-3/#orthogonal-flows">
<meta name="flags" content="" />
<style>
x-table {
display: table;
width: 300px;
height: 300px;
writing-mode: vertical-lr;
outline: 2px dashed blue;
}
x-caption {
display: table-caption;
height: 200px;
width: 200px;
writing-mode: horizontal-tb;
outline: 1px solid black;
}
</style>
<x-table>
<x-caption>caption</x-caption>
</x-table>
<script>
let body_height = document.querySelector("body").offsetHeight;
test(() => {
assert_greater_than(body_height, 0, "Vertical table with horizontal caption doesn't make <body> have crazy height.");
assert_less_than(body_height, 10000, "Vertical table with horizontal caption doesn't make <body> have crazy height.");
})
</script>

View file

@ -0,0 +1,4 @@
<!DOCTYPE html>
<div style="padding: 0.4px 0.6px">
<div style="width: 100px; height: 100px; border: 1px solid black"></div>
</div>

View file

@ -0,0 +1,12 @@
<!DOCTYPE html>
<link ref="help" href="https://drafts.csswg.org/css-transforms-2/#perspective-property">
<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#backface-visibility-property">
<link rel="match" href="subpixel-perspective-backface-hidden-ref.html">
<meta name="assert" content="Subpixel-positioned contents should not renderred the same regardless of perspective and backface-visibility:hidden">
<div style="padding: 0.4px 0.6px">
<div style="perspective: 1000px; backface-visibility: hidden">
<div style="width: 100px; height: 100px;
backface-visibility: hidden;border: 1px solid black">
</div>
</div>
</div>

View file

@ -0,0 +1,4 @@
<!DOCTYPE html>
<div style="padding: 0.6px 0.4px">
<div style="width: 100px; height: 100px; border: 1px solid black"></div>
</div>

View file

@ -0,0 +1,10 @@
<!DOCTYPE html>
<link ref="help" href="https://drafts.csswg.org/css-transforms-2/#perspective-property">
<link rel="help" href="https://drafts.csswg.org/css-transforms-2/#propdef-backface-visibility">
<link rel="match" href="subpixel-perspective-translate-z-0-ref.html">
<meta name="assert" content="Subpixel-positioned contents should be rendered the same regardless of perspective and translateZ(0)">
<div style="position: relative; top: 0.6px; left: 0.4px; width: 300px; height: 300px;
perspective: 1000px; overflow: hidden">
<div style="width: 100px; height: 100px;
transform: translateZ(0); border: 1px solid black"></div>
</div>

View file

@ -568,5 +568,11 @@ test(() => {
assert_equals(getComputedStyle(shadowDiv).color, "rgb(0, 0, 0)");
}, 'Adopting a shadow host will empty adoptedStyleSheets if adopting to a different document');
</script>
test(() => {
const host = document.createElement("div");
const root = host.attachShadow({mode: "open"});
root.adoptedStyleSheets = [new CSSStyleSheet()];
document.body.offsetTop;
}, 'Forcing a style update after adding an adopted stylesheet on a disconnected shadow root should not crash.');
</script>

View file

@ -21,7 +21,7 @@ you must either [fix all lint errors](#fixing-lint-errors), or you must
You must fix any errors the lint tool reports, unless an error is for
something essential to a certain test or that for some other
exceptional reason shouldn't prevent the test from being merged; in
those cases you can [whitelist test files](#updating-the-whiteslist)
those cases you can [whitelist test files](#updating-the-whitelist)
to suppress the errors. In all other cases, follow the instructions
below to fix all errors reported.

View file

@ -1,5 +1,6 @@
<!doctype html>
<meta charset="utf8">
<meta name="timeout" content="long">
<title>Events must dispatch on disabled elements</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>

View file

@ -5,9 +5,11 @@ if (typeof postMessage === 'function') {
onmessage = event => {
switch(event.data.type) {
case 'ready':
navigator.idle.query().then(
() => postMessage({ enabled: true }),
error => postMessage ({ enabled: false }));
new IdleDetector().start().then(() => {
postMessage({ enabled: true });
}, error => {
postMessage ({ enabled: false });
});
break;
}
};

View file

@ -1,9 +1,10 @@
<script>
'use strict';
navigator.idle.query().then(status => {
new IdleDetector().start().then(() => {
window.parent.postMessage({ enabled: true }, '*');
}, error => {
window.parent.postMessage({ enabled: false }, '*');
});
</script>

View file

@ -1,18 +1,23 @@
def main(request, response):
token = request.GET.first("token", None)
value = request.server.stash.take(token)
count = 0
if value != None:
count = int(value)
if request.GET.first("query", None) != None:
is_query = request.GET.first("query", None) != None
with request.server.stash.lock:
value = request.server.stash.take(token)
count = 0
if value != None:
count = int(value)
if is_query:
if count < 2:
request.server.stash.put(token, count)
else:
count = count + 1
request.server.stash.put(token, count)
if is_query:
headers = [("Count", count)]
content = ""
if count < 2:
request.server.stash.put(token, count)
return 200, headers, content
else:
count = count + 1
content = "body { background: rgb(0, 128, 0); }"
if count > 1:
content = "body { background: rgb(255, 0, 0); }"
@ -20,5 +25,4 @@ def main(request, response):
headers = [("Content-Type", "text/css"),
("Cache-Control", "private, max-age=0, stale-while-revalidate=60")]
request.server.stash.put(token, count)
return 200, headers, content

View file

@ -3,18 +3,24 @@ import os.path
def main(request, response):
token = request.GET.first("token", None)
value = request.server.stash.take(token)
count = 0
if value != None:
count = int(value)
if request.GET.first("query", None) != None:
is_query = request.GET.first("query", None) != None
with request.server.stash.lock:
value = request.server.stash.take(token)
count = 0
if value != None:
count = int(value)
if is_query:
if count < 2:
request.server.stash.put(token, count)
else:
count = count + 1
request.server.stash.put(token, count)
if is_query:
headers = [("Count", count)]
content = ""
if count < 2:
request.server.stash.put(token, count)
return 200, headers, content
else:
count = count + 1
filename = "green-16x16.png"
if count > 1:
filename = "green-256x256.png"
@ -22,7 +28,6 @@ def main(request, response):
path = os.path.join(os.path.dirname(__file__), "../../images", filename)
body = open(path, "rb").read()
request.server.stash.put(token, count)
response.add_required_headers = False
response.writer.write_status(200)
response.writer.write_header("content-length", len(body))

View file

@ -6,23 +6,27 @@ def id_token():
def main(request, response):
token = request.GET.first("token", None)
value = request.server.stash.take(token)
count = 0
if value != None:
count = int(value)
if request.GET.first("query", None) != None:
is_query = request.GET.first("query", None) != None
with request.server.stash.lock:
value = request.server.stash.take(token)
count = 0
if value != None:
count = int(value)
if is_query:
if count < 2:
request.server.stash.put(token, count)
else:
count = count + 1
request.server.stash.put(token, count)
if is_query:
headers = [("Count", count)]
content = ""
if count < 2:
request.server.stash.put(token, count)
return 200, headers, content
else:
count = count + 1
unique_id = id_token()
headers = [("Content-Type", "text/javascript"),
("Cache-Control", "private, max-age=0, stale-while-revalidate=60"),
("Unique-Id", unique_id)]
content = "report('{}')".format(unique_id)
request.server.stash.put(token, count)
return 200, headers, content

View file

@ -77,11 +77,8 @@ test1.step(function(){
test3.step(function(){
var c3 = new VTTCue(0, 2, "text3");
t1.addCue(c3);
assert_equals(t1.activeCues.length, 1, "t1.activeCues.length after adding a cue in the same script");
test3.step_timeout(function(){
assert_equals(t1.activeCues.length, 2, "t1.activeCues.length after the event loop has spun");
test3.done();
}, 0);
assert_equals(t1.activeCues.length, 2, "t1.activeCues.length should be changed immediately");
test3.done();
});
test2.done();
});

View file

@ -25,7 +25,7 @@
}
var cueCount = 0;
function cueEntered() {
function cueEntered(event) {
var currentCue = event.target;
// This cue is the currently active cue.

View file

@ -3,70 +3,76 @@
'use strict';
promise_test(async t => {
let promise = navigator.idle.query();
assert_equals(promise.constructor, Promise,
'query() returns a promise');
let status = new IdleDetector();
let status = await promise;
assert_true(status instanceof IdleStatus,
'query() promise resolves to an IdleStatus');
let watcher = new EventWatcher(t, status, ["change"]);
await status.start();
await watcher.wait_for("change");
assert_true(['active', 'idle'].includes(status.state.user),
'status has a valid user state');
assert_true(['locked', 'unlocked'].includes(status.state.screen),
'status has a valid screen state');
}, 'query() basics');
}, 'start() basics');
promise_test(async t => {
let used = false;
await navigator.idle.query({
new IdleDetector({
get threshold() {
used = true;
return 1;
}
});
assert_true(used, 'query() options "threshold" member was used');
}, 'query() uses threshold property');
assert_true(used, 'constructor options "threshold" member was used');
}, 'constructor uses threshold property');
promise_test(async t => {
return promise_rejects(
t,
new TypeError,
navigator.idle.query({threshold: 0}),
'Threshold of 0 should reject');
}, 'query() throws with invalid threshold (0)');
try {
new IdleDetector({threshold: 0});
assert_unreached('Threshold of 0 should reject');
} catch (error) {
assert_equals(error.name, 'TypeError');
}
}, 'constructor throws with invalid threshold (0)');
promise_test(async t => {
return promise_rejects(
t,
new TypeError,
navigator.idle.query({threshold: null}),
'Threshold of null should reject');
}, 'query() throws with invalid threshold (null)');
try {
new IdleDetector({threshold: null});
assert_unreached('Threshold of null should reject');
} catch (error) {
assert_equals(error.name, 'TypeError');
}
}, 'constructor throws with invalid threshold (null)');
promise_test(async t => {
return promise_rejects(
t,
new TypeError,
navigator.idle.query({threshold: -1}),
'Threshold of negative numbers should reject');
}, 'query() throws with invalid threshold (-1)');
try {
new IdleDetector({threshold: -1});
assert_unreached('Threshold of negative numbers should reject');
} catch (error) {
assert_equals(error.name, 'TypeError');
}
}, 'constructor throws with invalid threshold (-1)');
promise_test(async t => {
return promise_rejects(
t,
new TypeError,
navigator.idle.query({threshold: NaN}),
'Threshold of NaN should reject');
}, 'query() throws with invalid threshold (NaN)');
try {
new IdleDetector({threshold: NaN});
assert_unreached('Threshold of NaN should reject');
} catch (error) {
assert_equals(error.name, 'TypeError');
}
}, 'constructor throws with invalid threshold (NaN)');
promise_test(async t => {
return navigator.idle.query();
}, 'query() uses a default value for the threshold when none is passed');
new IdleDetector();
}, 'constructor uses a default value for the threshold when none is passed');
promise_test(async t => {
return navigator.idle.query({threshold: undefined});
}, 'query() uses a default value for the threshold');
new IdleDetector({threshold: undefined});
}, 'constructor uses a default value for the threshold');

View file

@ -19,25 +19,25 @@ const cross_origin_worker_frame_src = base_src + sub +
relative_worker_frame_path;
async_test(t => {
test_feature_availability('idle.query()', t, same_origin_src,
test_feature_availability('new IdleDetector().start()', t, same_origin_src,
expect_feature_available_default, 'idle-detection');
}, 'Attribute allow="idle-detection" in top-level frame ' +
'allows same-origin relocation.');
async_test(t => {
test_feature_availability('idle.query()', t, same_origin_worker_frame_src,
test_feature_availability('new IdleDetector().start()', t, same_origin_worker_frame_src,
expect_feature_available_default, 'idle-detection');
}, 'Attribute allow="idle-detection" in top-level frame ' +
'allows workers in same-origin relocation.');
async_test(t => {
test_feature_availability('idle.query()', t, cross_origin_src,
test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
expect_feature_unavailable_default, 'idle-detection');
}, 'Attribute allow="idle-detection" in top-level frame ' +
'disallows cross-origin relocation.');
async_test(t => {
test_feature_availability('idle.query()', t, cross_origin_worker_frame_src,
test_feature_availability('new IdleDetector().start()', t, cross_origin_worker_frame_src,
expect_feature_unavailable_default, 'idle-detection');
}, 'Attribute allow="idle-detection" in top-level frame ' +
'disallows workers in cross-origin relocation.');

View file

@ -15,25 +15,25 @@ const cross_origin_src = sub + same_origin_src;
const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
async_test(t => {
test_feature_availability('idle.query()', t, same_origin_src,
test_feature_availability('new IdleDetector().start()', t, same_origin_src,
expect_feature_available_default, 'idle-detection');
}, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
'in same-origin iframe using Feature policy "idle-detection".');
async_test(t => {
test_feature_availability('idle.query()', t, same_origin_worker_frame_src,
test_feature_availability('new IdleDetector().start()', t, same_origin_worker_frame_src,
expect_feature_available_default, 'idle-detection');
}, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
'in a worker in same-origin iframe using Feature policy "idle-detection".');
async_test(t => {
test_feature_availability('idle.query()', t, cross_origin_src,
test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
expect_feature_available_default, 'idle-detection');
}, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
'in cross-origin iframe using Feature policy "idle-detection".');
async_test(t => {
test_feature_availability('idle.query()', t, cross_origin_worker_frame_src,
test_feature_availability('new IdleDetector().start()', t, cross_origin_worker_frame_src,
expect_feature_available_default, 'idle-detection');
}, 'Attribute allow="idle-detection" in top-level frame can be enabled ' +
'in a worker in cross-origin iframe using Feature policy "idle-detection".');

View file

@ -14,31 +14,32 @@ const same_origin_worker_frame_src =
const cross_origin_src = sub + same_origin_src;
const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
promise_test(
() => navigator.idle.query(),
promise_test(async () => {
await new IdleDetector().start();
},
'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
'frame allows the top-level document.');
async_test(t => {
test_feature_availability('idle.query()', t, same_origin_src,
test_feature_availability('new IdleDetector().start()', t, same_origin_src,
expect_feature_available_default);
}, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
'frame allows same-origin iframes.');
async_test(t => {
test_feature_availability('idle.query()', t, same_origin_worker_frame_src,
test_feature_availability('new IdleDetector().start()', t, same_origin_worker_frame_src,
expect_feature_available_default);
}, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
'frame allows workers in same-origin iframes.');
async_test(t => {
test_feature_availability('idle.query()', t, cross_origin_src,
test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
expect_feature_available_default);
}, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
'frame allows cross-origin iframes.');
async_test(t => {
test_feature_availability('idle.query()', t, cross_origin_worker_frame_src,
test_feature_availability('new IdleDetector().start()', t, cross_origin_worker_frame_src,
expect_feature_available_default);
}, 'Feature-Policy {"idle-detection" : ["*"]} explicity set by top-level ' +
'frame allows workers in cross-origin iframes.');

View file

@ -11,19 +11,20 @@ const same_origin_src =
const cross_origin_src = 'https://{{domains[www]}}:{{ports[https][0]}}' +
same_origin_src;
promise_test(
() => navigator.idle.query(),
promise_test(async () => {
await new IdleDetector().start()
},
'Default "idle-detection" feature policy ["self"] ' +
'allows the top-level document.');
async_test(t => {
test_feature_availability('idle.query()', t, same_origin_src,
test_feature_availability('new IdleDetector().start()', t, same_origin_src,
expect_feature_available_default);
}, 'Default "idle-detection" feature policy ["self"] ' +
'allows same-origin iframes.');
async_test(t => {
test_feature_availability('idle.query()', t, cross_origin_src,
test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
expect_feature_unavailable_default);
}, 'Default "idle-detection" feature policy ["self"] ' +
'disallows cross-origin iframes.');

View file

@ -14,35 +14,37 @@ const same_origin_worker_frame_src =
const cross_origin_src = sub + same_origin_src;
const cross_origin_worker_frame_src = sub + same_origin_worker_frame_src;
promise_test(() => {
return navigator.idle.query().then(() => {
promise_test(async () => {
try {
let idleDetector = new IdleDetector();
await idleDetector.start();
assert_unreached('expected promise to reject with SecurityError');
}, error => {
} catch (error) {
assert_equals(error.name, 'SecurityError');
});
}
}, 'Feature-Policy {"idle-detection" : []} explicitly set by top-level frame ' +
'disallows query in the top-level document.');
async_test(t => {
test_feature_availability('idle.query()', t, same_origin_src,
test_feature_availability('new IdleDetector().start()', t, same_origin_src,
expect_feature_unavailable_default);
}, 'Feature-Policy {"idle-detection" : []} explicitly set by top-level frame ' +
'disallows same-origin iframes.');
async_test(t => {
test_feature_availability('idle.query()', t, same_origin_worker_frame_src,
test_feature_availability('new IdleDetector().start()', t, same_origin_worker_frame_src,
expect_feature_unavailable_default);
}, 'Feature-Policy {"idle-detection" : []} explicitly set by top-level frame ' +
'disallows workers in same-origin iframes.');
async_test(t => {
test_feature_availability('idle.query()', t, cross_origin_src,
test_feature_availability('new IdleDetector().start()', t, cross_origin_src,
expect_feature_unavailable_default);
}, 'Feature-Policy {"idle-detection" : []} explicitly set by top-level frame ' +
'disallows cross-origin iframes.');
async_test(t => {
test_feature_availability('idle.query()', t, cross_origin_worker_frame_src,
test_feature_availability('new IdleDetector().start()', t, cross_origin_worker_frame_src,
expect_feature_unavailable_default);
}, 'Feature-Policy {"idle-detection" : []} explicitly set by top-level frame ' +
'disallows workers in cross-origin iframes.');

View file

@ -1,28 +1,16 @@
[SecureContext]
interface mixin NavigatorIdle {
readonly attribute IdleManager idle;
};
Navigator includes NavigatorIdle;
WorkerNavigator includes NavigatorIdle;
[
SecureContext,
Exposed=(Window,Worker)
] interface IdleManager {
Promise<IdleStatus> query(optional IdleOptions options);
};
dictionary IdleOptions {
unsigned long threshold;
};
[
SecureContext,
Constructor(optional IdleOptions options),
Exposed=(Window,Worker)
] interface IdleStatus : EventTarget {
] interface IdleDetector : EventTarget {
readonly attribute IdleState state;
attribute EventHandler onchange;
Promise<any> start();
void stop();
};
[

View file

@ -1,34 +1,36 @@
// META: script=/resources/WebIDLParser.js
// META: script=/resources/idlharness.js
// https://github.com/inexorabletash/idle-detection
// https://github.com/samuelgoto/idle-detection
'use strict';
promise_test(async () => {
promise_test(async (t) => {
const srcs = ['./idle-detection.idl',
'/interfaces/dom.idl',
'/interfaces/html.idl'];
const [idle, dom, html] = await Promise.all(
srcs.map(i => fetch(i).then(r => r.text())));
srcs.map(i => fetch(i).then(r => r.text()))
);
const idl_array = new IdlArray();
idl_array.add_idls(idle);
idl_array.add_dependency_idls(dom);
idl_array.add_dependency_idls(html);
self.idle = await navigator.idle.query();
self.idle = new IdleDetector({threshold: 1});
let watcher = new EventWatcher(t, self.idle, ["change"]);
self.idle.start();
await watcher.wait_for("change");
idl_array.add_objects({
IdleManager: ['navigator.idle'],
IdleStatus: ['idle'],
IdleDetector: ['idle'],
IdleState: ['idle.state']
});
if (self.Window) {
idl_array.add_objects({ Navigator: ['navigator'] });
} else {
idl_array.add_objects({ WorkerNavigator: ['navigator'] });
}
idl_array.test();
}, 'Test IDL implementation of Idle Detection API');

View file

@ -1,19 +1,19 @@
<!DOCTYPE html>
<link rel="help" href="https://github.com/inexorabletash/idle-detection">
<link rel="help" href="https://github.com/samuelgoto/idle-detection">
<title>Tests the Idle Detection API</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/gen/layout_test_data/mojo/public/js/mojo_bindings.js"></script>
<script src="/gen/mojo/public/mojom/base/string16.mojom.js"></script>
<script src="/gen/mojo/public/mojom/base/time.mojom.js"></script>
<script src="/gen/third_party/blink/public/platform/modules/idle/idle_manager.mojom.js"></script>
<script src="/gen/third_party/blink/public/mojom/idle/idle_manager.mojom.js"></script>
<script src="mock.js"></script>
<script>
'use strict';
promise_test(async t => {
// Basic test that expects navigator.idle.query() to call internally
// addMonitor, which in turn will return an ACTIVE state.
// Basic test that expects start() to call internally
// addMonitor, which in turn return an ACTIVE state.
expect(addMonitor).andReturn((threshold, monitorPtr) => {
return Promise.resolve({
state: {
@ -23,36 +23,58 @@ promise_test(async t => {
});
});
let status = await navigator.idle.query({threshold: 10});
let detector = new IdleDetector({threshold: 10});
assert_equals(status.state.user, "active");
assert_equals(status.state.screen, "locked");
let watcher = new EventWatcher(t, detector, ["change"]);
await detector.start();
// Waits for the first event.
await watcher.wait_for("change");
assert_equals(detector.state.user, "active");
assert_equals(detector.state.screen, "locked");
detector.stop();
}, 'query()');
promise_test(async t => {
// Verifies that an event is thrown when a change of state from IDLE to ACTIVE
// is detected.
expect(addMonitor).andReturn((threshold, monitorPtr) => {
let first = Promise.resolve({
state: {
user: UserIdleState.ACTIVE,
screen: ScreenIdleState.UNLOCKED
}
});
t.step_timeout(() => {
monitorPtr.update({
user: UserIdleState.IDLE,
screen: ScreenIdleState.UNLOCKED
});
}, 0);
return Promise.resolve({
state: {
user: UserIdleState.ACTIVE,
screen: ScreenIdleState.UNLOCKED
}
});
return first;
});
let monitor = await navigator.idle.query({threshold: 10});
let detector = new IdleDetector({threshold: 10});
await new EventWatcher(t, monitor, ["change"]).wait_for("change");
let watcher = new EventWatcher(t, detector, ["change"]);
assert_equals(monitor.state.user, "idle");
assert_equals(monitor.state.screen, "unlocked");
await detector.start();
// Wait for the initial state.
await watcher.wait_for("change");
// Wait for the first change in state.
await watcher.wait_for("change");
assert_equals(detector.state.user, "idle");
assert_equals(detector.state.screen, "unlocked");
detector.stop();
}, 'updates once');
@ -60,6 +82,13 @@ promise_test(async t => {
// Simulates the user being active, going idle and then going back active
// again.
expect(addMonitor).andReturn((threshold, monitorPtr) => {
let first = Promise.resolve({
state: {
user: UserIdleState.ACTIVE,
screen: ScreenIdleState.UNLOCKED
}
});
// Updates the client once with the user idle.
t.step_timeout(() => {
monitorPtr.update({
@ -74,25 +103,27 @@ promise_test(async t => {
screen: ScreenIdleState.UNLOCKED
});
}, 1);
return Promise.resolve({
state: {
user: UserIdleState.ACTIVE,
screen: ScreenIdleState.UNLOCKED
}
});
return first;
});
let monitor = await navigator.idle.query({threshold: 10});
let detector = new IdleDetector({threshold: 10});
let watcher = new EventWatcher(t, monitor, ["change"]);
let watcher = new EventWatcher(t, detector, ["change"]);
// waits for the first event.
await detector.start();
// Waits for the initial state.
await watcher.wait_for("change");
assert_equals(monitor.state.user, "idle");
// waits for the second event.
// Waits for the first event.
await watcher.wait_for("change");
assert_equals(monitor.state.user, "active");
assert_equals(detector.state.user, "idle");
// Waits for the second event.
await watcher.wait_for("change");
assert_equals(detector.state.user, "active");
detector.stop();
}, 'updates twice');
promise_test(async t => {
@ -106,28 +137,140 @@ promise_test(async t => {
});
});
let monitor = await navigator.idle.query({threshold: 10});
let detector = new IdleDetector({threshold: 10});
assert_equals(monitor.state.screen, "locked");
let watcher = new EventWatcher(t, detector, ["change"]);
await detector.start();
// waits for the initial state.
await watcher.wait_for("change");
assert_equals(detector.state.screen, "locked");
detector.stop();
}, 'locked screen');
promise_test(async t => {
// Simulates the service becoming unavailable.
expect(addMonitor).andReturn((threshold, monitorPtr) => {
return new Promise((resolve, reject) => {
// leave the renderer deliberately hanging by not resolve()-ing.
return Promise.resolve({
state: {
user: UserIdleState.ACTIVE,
screen: ScreenIdleState.LOCKED
}
});
});
let detector = new IdleDetector({threshold: 10});
let event = new Promise((resolve, reject) => {
detector.onchange = resolve;
});
await detector.start();
// Waits for the first event.
await event;
assert_equals(detector.state.user, "active");
assert_equals(detector.state.screen, "locked");
detector.stop();
}, 'IdleDetector.onchange');
promise_test(async t => {
expect(addMonitor).andReturn((threshold, monitorPtr) => {
return Promise.resolve({
state: {
user: UserIdleState.ACTIVE,
screen: ScreenIdleState.UNLOCKED
}
});
});
let error = new Promise((resolve, reject) => {
navigator.idle.query({threshold: 10})
.then((e) => {reject("unexpected response :(")})
.catch((e) => {resolve(e.message)});
});
let detector = new IdleDetector({threshold: 10});
// simulates what happens when the service is unavailable.
close();
let watcher = new EventWatcher(t, detector, ["change"]);
assert_equals(await error, "Idle detection not available");
}, "service unavailable");
</script>
// Calling start() multiple times should be safe.
await detector.start();
await detector.start();
await detector.start();
await detector.start();
// waits for the initial state.
await watcher.wait_for("change");
assert_equals(detector.state.user, "active");
assert_equals(detector.state.screen, "unlocked");
// Calling stop() multiple times should be safe.
detector.stop();
detector.stop();
detector.stop();
detector.stop();
}, 'Safe to call start() or stop() multiple times');
promise_test(async t => {
expect(addMonitor).andReturn((threshold, monitorPtr) => {
return Promise.resolve({
state: {
user: UserIdleState.ACTIVE,
screen: ScreenIdleState.UNLOCKED
}
});
});
let detector = new IdleDetector({threshold: 10});
// Calling stop() before start() is a no-op.
detector.stop();
let watcher = new EventWatcher(t, detector, ["change"]);
await detector.start();
// waits for the initial state.
await watcher.wait_for("change");
assert_equals(detector.state.user, "active");
assert_equals(detector.state.screen, "unlocked");
detector.stop();
}, 'Calling stop() after start() is a no-op');
promise_test(async t => {
expect(addMonitor).andReturn((threshold, monitorPtr) => {
return Promise.resolve({
state: {
user: UserIdleState.ACTIVE,
screen: ScreenIdleState.UNLOCKED
}
});
});
let detector = new IdleDetector({threshold: 10});
let watcher = new EventWatcher(t, detector, ["change"]);
await detector.start();
await watcher.wait_for("change");
detector.stop();
expect(addMonitor).andReturn((threshold, monitorPtr) => {
return Promise.resolve({
state: {
user: UserIdleState.IDLE,
screen: ScreenIdleState.LOCKED
}
});
});
// Restarting the monitor.
await detector.start();
await watcher.wait_for("change");
assert_equals(detector.state.user, "idle");
assert_equals(detector.state.screen, "locked");
detector.stop();
}, 'Calling start() after stop(): re-starting monitor.');
</script>

View file

@ -8,7 +8,9 @@ if (typeof postMessage === 'function') {
workerType = 'dedicated';
}
promise_test(() => navigator.idle.query(),
promise_test(async () => {
await new IdleDetector().start()
},
`Inherited header feature policy allows ${workerType} workers.`)
done();

View file

@ -9,9 +9,14 @@ if (typeof postMessage === 'function') {
workerType = 'dedicated';
}
promise_test(() => navigator.idle.query().then(
() => assert_unreached('expected promise to reject with SecurityError'),
error => assert_equals(error.name, 'SecurityError')),
`Inherited ${header} disallows ${workerType} workers.`);
promise_test(async () => {
try {
await new IdleDetector().start();
assert_unreached('expected start() to throw with SecurityError');
} catch (error) {
assert_equals(error.name, 'SecurityError');
}
},
`Inherited ${header} disallows ${workerType} workers.`);
done();

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/test-helper.js"></script>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';">
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=B",
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=C": "../resources/log.js?pipe=sub&name=D"
}
}
</script>
<script>
promise_test(t => {
return promise_rejects(t, TypeError(),
import('../resources/log.js?pipe=sub&name=A'),
'Dynamic import should fail');
}, 'The URL after mapping violates CSP (but not the URL before mapping)');
promise_test(t => {
return import('https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=C')
.then(() => assert_array_equals(log, ["log:D"]));
}, 'The URL before mapping violates CSP (but not the URL after mapping)');
</script>

View file

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="../resources/test-helper.js"></script>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';">
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=B",
"https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=C": "../resources/log.js?pipe=sub&name=D"
}
}
</script>
<script type="module">
import '../resources/log.js?pipe=sub&name=A';
</script>
<script type="module">
test(t => {
assert_array_equals(log, []);
}, 'The URL after mapping violates CSP (but not the URL before mapping)');
</script>
<script type="module">
import 'https://{{domains[www1]}}:{{ports[https][0]}}/import-maps/resources/log.js?pipe=sub&name=C';
</script>
<script type="module">
test(t => {
assert_array_equals(log, ["log:D"]);
}, 'The URL before mapping violates CSP (but not the URL after mapping)');
</script>

View file

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-wrong9e+pZbSYIkpB8BIE0Hs7yHajJDiX5mnT/wrong=' 'sha256-RAsyam34o4peVe9sCebtaZWRVhqAhudem+NlcnP2Kp8=';">
<!-- 'sha256-P5xqp9e+pZbSYIkpB8BIE0Hs7yHajJDiX5mnT/1PO1I=' -->
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
}
}
</script>
<!-- 'sha256-RAsyam34o4peVe9sCebtaZWRVhqAhudem+NlcnP2Kp8=' -->
<script>
const log = [];
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:A"]))
},
'Importmap should not be accepted due to wrong hash');
</script>

View file

@ -0,0 +1,24 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'sha256-P5xqp9e+pZbSYIkpB8BIE0Hs7yHajJDiX5mnT/1PO1I=' 'sha256-Ciqph+wQDoB2suzqZVHOD0iw99WqaTUwZXRl7ATzBxc=';">
<!-- 'sha256-P5xqp9e+pZbSYIkpB8BIE0Hs7yHajJDiX5mnT/1PO1I=' -->
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
}
}
</script>
<!-- 'sha256-Ciqph+wQDoB2suzqZVHOD0iw99WqaTUwZXRl7ATzBxc=' -->
<script>
const log = [];
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:B"]))
},
'Importmap should be accepted due to hash');
</script>

View file

@ -0,0 +1,27 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-abc';">
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
}
}
</script>
<script type="importmap" nonce="wrong">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
}
}
</script>
<script nonce="abc">
const log = [];
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:A"]))
},
'Importmap should be rejected due to nonce');
</script>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<meta http-equiv="Content-Security-Policy" content="script-src 'nonce-abc';">
<script type="importmap" nonce="abc">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
}
}
</script>
<script nonce="abc">
const log = [];
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:B"]))
},
'Importmap should be accepted according to its correct nonce');
</script>

View file

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<meta http-equiv="Content-Security-Policy" content="script-src 'self' 'unsafe-inline';">
<script type="importmap">
{
"imports": {
"../resources/log.js?pipe=sub&name=A": "../resources/log.js?pipe=sub&name=B"
}
}
</script>
<script>
const log = [];
promise_test(() => {
return import("../resources/log.js?pipe=sub&name=A")
.then(() => assert_array_equals(log, ["log:B"]))
},
'Importmap should be accepted due to unsafe-inline');
</script>

View file

@ -0,0 +1,9 @@
[actionsWithKeyPressed.html]
expected:
if product == "safari" or product == "firefox": ERROR
[TestDriver actions: actions with key pressed]
expected:
if product == "chrome": FAIL

View file

@ -0,0 +1,4 @@
[generate_test_report.html]
expected:
if product == "firefox": ERROR
if product == "safari": ERROR

View file

@ -0,0 +1,67 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>TestDriver actions: actions with key pressed</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-actions.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<style>
div#test1, div#test2 {
position: fixed;
top: 0;
left: 0;
width: 100px;
height: 100px;
background-color: blue;
}
div#test2 {
position: fixed;
top: 100px;
left: 0;
width: 100px;
height: 100px;
background-color: green;
}
</style>
<div id="test1">
</div>
<div id="test2">
</div>
<script>
let keys = [];
async_test(t => {
let test1 = document.getElementById("test1");
let test2 = document.getElementById("test2");
document.getElementById("test1").addEventListener("click",
e => {keys.push(e.getModifierState("Control"))});
document.getElementById("test2").addEventListener("click",
e => {keys.push(e.getModifierState("Control"))});
let actions = new test_driver.Actions()
.keyDown("\uE009")
.addTick()
.pointerMove(0, 0, {origin: test1})
.pointerDown()
.pointerUp()
.pointerMove(0, 0, {origin: test2})
.pointerDown()
.pointerUp()
.addTick()
.keyUp("\uE009")
.addTick()
.pointerMove(0, 0, {origin: test1})
.pointerDown()
.pointerUp();
actions.send()
.then(t.step_func_done(() => assert_array_equals(keys, [true, true, false])))
.catch(e => t.step_func(() => assert_unreached("Actions sequence failed " + e)));
});
</script>

View file

@ -0,0 +1,16 @@
<!DOCTYPE html>
<meta charset="utf-8">
<title>TestDriver generate_test_report method</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script>
<script>
async_test(t => {
test_driver
.generate_test_report("Test message.")
.then(() => t.done())
.catch(t.unreached_func("generate_test_report failed"));
});
</script>

View file

@ -1,30 +0,0 @@
// GENERATED CONTENT - DO NOT EDIT
// Content was automatically extracted by Reffy into reffy-reports
// (https://github.com/tidoust/reffy-reports)
// Source: Resize Observer 1 (https://wicg.github.io/ResizeObserver/)
[Constructor(ResizeObserverCallback callback),
Exposed=Window]
interface ResizeObserver {
void observe(Element target);
void unobserve(Element target);
void disconnect();
};
callback ResizeObserverCallback = void (sequence<ResizeObserverEntry> entries, ResizeObserver observer);
[Constructor(Element target)
]
interface ResizeObserverEntry {
readonly attribute Element target;
readonly attribute DOMRectReadOnly contentRect;
};
[Constructor(Element target)
]
interface ResizeObservation {
readonly attribute Element target;
readonly attribute float broadcastWidth;
readonly attribute float broadcastHeight;
boolean isActive();
};

View file

@ -25,7 +25,7 @@ interface NamedFlow : EventTarget {
sequence<Region> getRegionsByContent(Node node);
};
interface Region {
interface mixin Region {
readonly attribute CSSOMString regionOverset;
sequence<Range>? getRegionFlowRanges();
};

View file

@ -37,7 +37,7 @@ partial interface CSSStyleRule {
[SameObject] readonly attribute StylePropertyMap styleMap;
};
partial interface ElementCSSInlineStyle {
partial interface mixin ElementCSSInlineStyle {
[SameObject] readonly attribute StylePropertyMap attributeStyleMap;
};

View file

@ -59,9 +59,11 @@ partial dictionary MediaTrackSupportedConstraints {
boolean brightness = true;
boolean contrast = true;
boolean pan = true;
boolean saturation = true;
boolean sharpness = true;
boolean focusDistance = true;
boolean tilt = true;
boolean zoom = true;
boolean torch = true;
};
@ -82,6 +84,8 @@ partial dictionary MediaTrackCapabilities {
MediaSettingsRange sharpness;
MediaSettingsRange focusDistance;
MediaSettingsRange pan;
MediaSettingsRange tilt;
MediaSettingsRange zoom;
boolean torch;
@ -104,6 +108,8 @@ partial dictionary MediaTrackConstraintSet {
ConstrainDouble sharpness;
ConstrainDouble focusDistance;
ConstrainDouble pan;
ConstrainDouble tilt;
ConstrainDouble zoom;
ConstrainBoolean torch;
@ -126,6 +132,8 @@ partial dictionary MediaTrackSettings {
double sharpness;
double focusDistance;
double pan;
double tilt;
double zoom;
boolean torch;

View file

@ -0,0 +1,43 @@
// GENERATED CONTENT - DO NOT EDIT
// Content was automatically extracted by Reffy into reffy-reports
// (https://github.com/tidoust/reffy-reports)
// Source: Resize Observer (https://drafts.csswg.org/resize-observer/)
enum ResizeObserverBoxOptions {
"border-box", "content-box"
};
dictionary ResizeObserverOptions {
ResizeObserverBoxOptions box = "content-box";
};
[Exposed=(Window),
Constructor(ResizeObserverCallback callback)]
interface ResizeObserver {
void observe(Element target, optional ResizeObserverOptions options);
void unobserve(Element target);
void disconnect();
};
callback ResizeObserverCallback = void (sequence<ResizeObserverEntry> entries, ResizeObserver observer);
[Exposed=Window, Constructor(Element target)]
interface ResizeObserverEntry {
readonly attribute Element target;
readonly attribute DOMRectReadOnly contentRect;
readonly attribute ResizeObserverSize borderBoxSize;
readonly attribute ResizeObserverSize contentBoxSize;
};
interface ResizeObserverSize {
readonly attribute unrestricted double inlineSize;
readonly attribute unrestricted double blockSize;
};
[Constructor(Element target)
]
interface ResizeObservation {
readonly attribute Element target;
readonly attribute ResizeObserverBoxOptions observedBox;
readonly attribute ResizeObserverSize lastReportedSize;
};

View file

@ -10,15 +10,15 @@ dictionary PerformanceMarkOptions {
dictionary PerformanceMeasureOptions {
any detail = null;
(DOMString or DOMHighResTimeStamp) startTime;
(DOMString or DOMHighResTimeStamp) start;
DOMHighResTimeStamp duration;
(DOMString or DOMHighResTimeStamp) endTime;
(DOMString or DOMHighResTimeStamp) end;
};
partial interface Performance {
PerformanceMark mark(DOMString markName, optional PerformanceMarkOptions markOptions);
void clearMarks(optional DOMString markName);
PerformanceMeasure measure(DOMString measureName, optional (DOMString or PerformanceMeasureOptions) startOrMeasureOptions, optional DOMString endMark);
PerformanceMeasure measure(DOMString measureName, optional (DOMString or PerformanceMeasureOptions)? startOrMeasureOptions, optional DOMString endMark);
void clearMeasures(optional DOMString measureName);
};

View file

@ -1,7 +1,7 @@
// GENERATED CONTENT - DO NOT EDIT
// Content was automatically extracted by Reffy into reffy-reports
// (https://github.com/tidoust/reffy-reports)
// Source: Web Authentication: An API for accessing Public Key Credentials - Level 1 (https://w3c.github.io/webauthn/)
// Source: Web Authentication: An API for accessing Public Key Credentials - Level 2 (https://w3c.github.io/webauthn/)
[SecureContext, Exposed=Window]
interface PublicKeyCredential : Credential {
@ -30,6 +30,7 @@ interface AuthenticatorResponse {
[SecureContext, Exposed=Window]
interface AuthenticatorAttestationResponse : AuthenticatorResponse {
[SameObject] readonly attribute ArrayBuffer attestationObject;
sequence<AuthenticatorTransport> getTransports();
};
[SecureContext, Exposed=Window]

View file

@ -20,7 +20,8 @@ enum RTCStatsType {
"candidate-pair",
"local-candidate",
"remote-candidate",
"certificate"
"certificate",
"stunserverconnection"
};
dictionary RTCRtpStreamStats : RTCStats {
@ -28,11 +29,6 @@ dictionary RTCRtpStreamStats : RTCStats {
DOMString kind;
DOMString transportId;
DOMString codecId;
unsigned long firCount;
unsigned long pliCount;
unsigned long nackCount;
unsigned long sliCount;
unsigned long long qpSum;
};
dictionary RTCCodecStats : RTCStats {
@ -72,13 +68,19 @@ dictionary RTCInboundRtpStreamStats : RTCReceivedRtpStreamStats {
DOMString receiverId;
DOMString remoteId;
unsigned long framesDecoded;
unsigned long long qpSum;
DOMHighResTimeStamp lastPacketReceivedTimestamp;
double averageRtcpInterval;
unsigned long fecPacketsReceived;
unsigned long fecPacketsDiscarded;
unsigned long long bytesReceived;
unsigned long packetsFailedDecryption;
unsigned long packetsDuplicated;
record<USVString, unsigned long> perDscpPacketsReceived;
unsigned long nackCount;
unsigned long firCount;
unsigned long pliCount;
unsigned long sliCount;
};
dictionary RTCRemoteInboundRtpStreamStats : RTCReceivedRtpStreamStats {
@ -102,11 +104,16 @@ dictionary RTCOutboundRtpStreamStats : RTCSentRtpStreamStats {
DOMHighResTimeStamp lastPacketSentTimestamp;
double targetBitrate;
unsigned long framesEncoded;
unsigned long long qpSum;
double totalEncodeTime;
double averageRtcpInterval;
RTCQualityLimitationReason qualityLimitationReason;
record<DOMString, double> qualityLimitationDurations;
record<USVString, unsigned long> perDscpPacketsSent;
unsigned long nackCount;
unsigned long firCount;
unsigned long pliCount;
unsigned long sliCount;
};
enum RTCQualityLimitationReason {
@ -198,6 +205,7 @@ dictionary RTCAudioReceiverStats : RTCAudioHandlerStats {
unsigned long long jitterBufferEmittedCount;
unsigned long long totalSamplesReceived;
unsigned long long concealedSamples;
unsigned long long silentConcealedSamples;
unsigned long long concealmentEvents;
};
@ -298,6 +306,16 @@ dictionary RTCCertificateStats : RTCStats {
DOMString issuerCertificateId;
};
dictionary RTCStunServerConnectionStats : RTCStats {
DOMString url;
long port;
DOMString protocol;
RTCNetworkType networkType;
unsigned long totalRequestsSent;
unsigned long totalResponsesReceived;
double totalRoundTripTime;
};
partial dictionary RTCIceCandidateStats {
boolean isRemote;
};
@ -312,3 +330,7 @@ partial dictionary RTCRtpStreamStats {
DOMString mediaType;
double averageRTCPInterval;
};
partial dictionary RTCInboundRtpStreamStats {
double fractionLost;
};

View file

@ -25,7 +25,6 @@ enum XREnvironmentBlendMode {
[SecureContext, Exposed=Window] interface XRSession : EventTarget {
// Attributes
readonly attribute XRSessionMode mode;
readonly attribute XRPresentationContext? outputContext;
readonly attribute XREnvironmentBlendMode environmentBlendMode;
readonly attribute XRRenderState renderState;
readonly attribute XRSpace viewerSpace;
@ -59,19 +58,20 @@ enum XRSessionMode {
dictionary XRSessionCreationOptions {
XRSessionMode mode = "inline";
XRPresentationContext? outputContext = null;
};
dictionary XRRenderStateInit {
double depthNear;
double depthFar;
XRLayer? baseLayer;
XRPresentationContext? outputContext;
};
[SecureContext, Exposed=Window] interface XRRenderState {
readonly attribute double depthNear;
readonly attribute double depthFar;
readonly attribute XRLayer? baseLayer;
readonly attribute XRPresentationContext? outputContext;
};
callback XRFrameRequestCallback = void (DOMHighResTimeStamp time, XRFrame frame);

View file

@ -804,4 +804,5 @@ CSS-COLLIDING-REF-NAME: css/vendor-imports/mozilla/mozilla-central-reftests/cont
# Signed Exchange files have hard-coded URLs in the certUrl field
WEB-PLATFORM.TEST:signed-exchange/resources/*.sxg
WEB-PLATFORM.TEST:signed-exchange/appcache/resources/*.sxg
WEB-PLATFORM.TEST:signed-exchange/resources/generate-test-sxgs.sh

View file

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html>
<head>
<title>&lt;mo&gt; paint lspace rspace</title>
<meta charset="utf-8">
</head>
<body>
<h1>LTR case</h1>
<p>The test passes if the arrow has a leading space of 100px, which is as wide as the black block to the left,
and a trailing space of 200px, which is as wide as the black block to the right.</p>
<math>
<mspace width="100px" height="10px" depth="10px" style="background: black"></mspace>
<mspace width="100px"></mspace>
<mo lspace="0px" rspace="0px"></mo>
<mspace width="200px"></mspace>
<mspace width="200px" height="10px" depth="10px" style="background: black"></mspace>
</math>
<p>The test passes if the arrow has a leading space of 150px, which is as wide as the black block to the left,
and a trailing space of 150px, which is as wide as the black block to the right.</p>
<math>
<mspace width="150px" height="10px" depth="10px" style="background: black"></mspace>
<mspace width="150px"></mspace>
<mo lspace="0px" rspace="0px"></mo>
<mspace width="150px"></mspace>
<mspace width="150px" height="10px" depth="10px" style="background: black"></mspace>
</math>
<p>The test passes if the arrow has a leading space of 200px, which is as wide as the black block to the left,
and a trailing space of 100px, which is as wide as the black block to the right.</p>
<math>
<mspace width="200px" height="10px" depth="10px" style="background: black"></mspace>
<mspace width="200px"></mspace>
<mo lspace="0px" rspace="0px"></mo>
<mspace width="100px"></mspace>
<mspace width="100px" height="10px" depth="10px" style="background: black"></mspace>
</math>
<h1>RTL case</h1>
<p>The test passes if the arrow has a leading space of 100px, which is as wide as the black block to the right,
and a trailing space of 200px, which is as wide as the black block to the left.</p>
<math dir="rtl">
<mspace width="100px" height="10px" depth="10px" style="background: black"></mspace>
<mspace width="100px"></mspace>
<mo lspace="0px" rspace="0px"></mo>
<mspace width="200px"></mspace>
<mspace width="200px" height="10px" depth="10px" style="background: black"></mspace>
</math>
<p>The test passes if the arrow has a leading space of 150px, which is as wide as the black block to the right,
and a trailing space of 150px, which is as wide as the black block to the left.</p>
<math dir="rtl">
<mspace width="150px" height="10px" depth="10px" style="background: black"></mspace>
<mspace width="150px"></mspace>
<mo lspace="0px" rspace="0px"></mo>
<mspace width="150px"></mspace>
<mspace width="150px" height="10px" depth="10px" style="background: black"></mspace>
</math>
<p>The test passes if the arrow has a leading space of 200px, which is as wide as the black block to the right,
and a trailing space of 100px, which is as wide as the black block to the left.</p>
<math dir="rtl">
<mspace width="200px" height="10px" depth="10px" style="background: black"></mspace>
<mspace width="200px"></mspace>
<mo lspace="0px" rspace="0px"></mo>
<mspace width="100px"></mspace>
<mspace width="100px" height="10px" depth="10px" style="background: black"></mspace>
</math>
</body>
</html>

View file

@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>&lt;mo&gt; paint lspace rspace</title>
<link rel="help" href="https://mathml-refresh.github.io/mathml-core/#tokenmo">
<meta name="assert" content="Verifies values for lspace and rspace for element mo in LTR and RTL modes.">
<link rel="match" href="mo-paint-lspace-rspace-ref.html">
</head>
<body>
<h1>LTR case</h1>
<p>The test passes if the arrow has a leading space of 100px, which is as wide as the black block to the left,
and a trailing space of 200px, which is as wide as the black block to the right.</p>
<mth>
<mspace width="100px" height="10px" depth="10px" style="background: black"></mspace>
<mo lspace="100px" rspace="200px"></mo>
<mspace width="200px" height="10px" depth="10px" style="background: black"></mspace>
</math>
<p>The test passes if the arrow has a leading space of 150px, which is as wide as the black block to the left,
and a trailing space of 150px, which is as wide as the black block to the right.</p>
<math>
<mspace width="150px" height="10px" depth="10px" style="background: black"></mspace>
<mo lspace="150px" rspace="150px"></mo>
<mspace width="150px" height="10px" depth="10px" style="background: black"></mspace>
</math>
<p>The test passes if the arrow has a leading space of 200px, which is as wide as the black block to the left,
and a trailing space of 100px, which is as wide as the black block to the right.</p>
<math>
<mspace width="200px" height="10px" depth="10px" style="background: black"></mspace>
<mo lspace="200px" rspace="100px"></mo>
<mspace width="100px" height="10px" depth="10px" style="background: black"></mspace>
</math>
<h1>RTL case</h1>
<p>The test passes if the arrow has a leading space of 100px, which is as wide as the black block to the right,
and a trailing space of 200px, which is as wide as the black block to the left.</p>
<math dir="rtl">
<mspace width="100px" height="10px" depth="10px" style="background: black"></mspace>
<mo lspace="100px" rspace="200px"></mo>
<mspace width="200px" height="10px" depth="10px" style="background: black"></mspace>
</math>
<p>The test passes if the arrow has a leading space of 150px, which is as wide as the black block to the right,
and a trailing space of 150px, which is as wide as the black block to the left.</p>
<math dir="rtl">
<mspace width="150px" height="10px" depth="10px" style="background: black"></mspace>
<mo lspace="150px" rspace="150px"></mo>
<mspace width="150px" height="10px" depth="10px" style="background: black"></mspace>
</math>
<p>The test passes if the arrow has a leading space of 200px, which is as wide as the black block to the right,
and a trailing space of 100px, which is as wide as the black block to the left.</p>
<math dir="rtl">
<mspace width="200px" height="10px" depth="10px" style="background: black"></mspace>
<mo lspace="200px" rspace="100px"></mo>
<mspace width="100px" height="10px" depth="10px" style="background: black"></mspace>
</math>
</body>
</html>

View file

@ -0,0 +1,162 @@
<!DOCTYPE html>
<!-- Copyright © 2019 Igalia. -->
<html>
<head>
<title>Frame checking test for MSE playback in presence of a reappend.</title>
<meta name="timeout" content="long">
<meta name="charset" content="UTF-8">
<link rel="author" title="Alicia Boya García" href="mailto:aboya@igalia.com">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="mediasource-util.js"></script>
</head>
<body>
<div id="log"></div>
<canvas id="test-canvas"></canvas>
<script>
function waitForEventPromise(element, event) {
return new Promise(resolve => {
function handler(ev) {
element.removeEventListener(event, handler);
resolve(ev);
}
element.addEventListener(event, handler);
});
}
function appendBufferPromise(sourceBuffer, data) {
sourceBuffer.appendBuffer(data);
return waitForEventPromise(sourceBuffer, "update");
}
function waitForPlayerToReachTimePromise(mediaElement, time) {
return new Promise(resolve => {
function timeupdate() {
if (mediaElement.currentTime < time)
return;
mediaElement.removeEventListener("timeupdate", timeupdate);
resolve();
}
mediaElement.addEventListener("timeupdate", timeupdate);
});
}
function readPixel(imageData, x, y) {
return {
r: imageData.data[4 * (y * imageData.width + x)],
g: imageData.data[1 + 4 * (y * imageData.width + x)],
b: imageData.data[2 + 4 * (y * imageData.width + x)],
a: imageData.data[3 + 4 * (y * imageData.width + x)],
};
}
function isPixelLit(pixel) {
const threshold = 200; // out of 255
return pixel.r >= threshold && pixel.g >= threshold && pixel.b >= threshold;
}
// The test video has a few gray boxes. Each box interval (1 second) a new box is lit white and a different note
// is played. This test makes sure the right number of lit boxes and the right note are played at the right time.
const totalBoxes = 7;
const boxInterval = 1; // seconds
const videoWidth = 320;
const videoHeight = 240;
const boxesY = 210;
const boxSide = 20;
const boxMargin = 20;
const allBoxesWidth = totalBoxes * boxSide + (totalBoxes - 1) * boxMargin;
const boxesX = new Array(totalBoxes).fill(undefined)
.map((_, i) => (videoWidth - allBoxesWidth) / 2 + boxSide / 2 + i * (boxSide + boxMargin));
// Sound starts playing A4 (440 Hz) and goes one chromatic note up with every box lit.
// By comparing the player position to both the amount of boxes lit and the note played we can detect A/V
// synchronization issues automatically.
const noteFrequencies = new Array(1 + totalBoxes).fill(undefined)
.map((_, i) => 440 * Math.pow(Math.pow(2, 1 / 12), i));
// We also check the first second [0, 1) where no boxes are lit, therefore we start counting at -1 to do the check
// for zero lit boxes.
let boxesLitSoFar = -1;
mediasource_test(async function (test, mediaElement, mediaSource) {
const canvas = document.getElementById("test-canvas");
const canvasCtx = canvas.getContext("2d");
canvas.width = videoWidth;
canvas.height = videoHeight;
const videoData = await (await fetch("mp4/test-boxes-video.mp4")).arrayBuffer();
const audioData = (await (await fetch("mp4/test-boxes-audio.mp4")).arrayBuffer());
const videoSb = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.4d401f"');
const audioSb = mediaSource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
mediaElement.addEventListener('ended', onEnded);
mediaElement.addEventListener('timeupdate', onTimeUpdate);
await appendBufferPromise(videoSb, videoData);
await appendBufferPromise(audioSb, audioData);
mediaElement.play();
audioCtx = new (window.AudioContext || window.webkitAudioContext)();
source = audioCtx.createMediaElementSource(mediaElement);
analyser = audioCtx.createAnalyser();
analyser.fftSize = 8192;
source.connect(analyser);
analyser.connect(audioCtx.destination);
const freqDomainArray = new Float32Array(analyser.frequencyBinCount);
function checkNoteBeingPlayed() {
const expectedNoteFrequency = noteFrequencies[boxesLitSoFar];
analyser.getFloatFrequencyData(freqDomainArray);
const maxBin = freqDomainArray.reduce((prev, curValue, i) =>
curValue > prev.value ? {index: i, value: curValue} : prev,
{index: -1, value: -Infinity});
const binFrequencyWidth = audioCtx.sampleRate / analyser.fftSize;
const binFreq = maxBin.index * binFrequencyWidth;
assert_true(Math.abs(expectedNoteFrequency - binFreq) <= binFrequencyWidth,
`The note being played matches the expected one (boxes lit: ${boxesLitSoFar}, ${expectedNoteFrequency.toFixed(1)} Hz)` +
`, found ~${binFreq.toFixed(1)} Hz`);
}
function countLitBoxesInCurrentVideoFrame() {
canvasCtx.drawImage(mediaElement, 0, 0);
const imageData = canvasCtx.getImageData(0, 0, videoWidth, videoHeight);
const lights = boxesX.map(boxX => isPixelLit(readPixel(imageData, boxX, boxesY)));
let litBoxes = 0;
for (let i = 0; i < lights.length; i++) {
if (lights[i])
litBoxes++;
}
for (let i = litBoxes; i < lights.length; i++) {
assert_false(lights[i], 'After the first non-lit box, all boxes must non-lit');
}
return litBoxes;
}
await waitForPlayerToReachTimePromise(mediaElement, 2.5);
await appendBufferPromise(audioSb, audioData);
mediaSource.endOfStream();
function onTimeUpdate() {
const graceTime = 0.5;
if (mediaElement.currentTime >= (1 + boxesLitSoFar) * boxInterval + graceTime && boxesLitSoFar < totalBoxes) {
assert_equals(countLitBoxesInCurrentVideoFrame(), boxesLitSoFar + 1, "Num of lit boxes:");
boxesLitSoFar++;
checkNoteBeingPlayed();
}
}
function onEnded() {
assert_equals(boxesLitSoFar, totalBoxes, "Boxes lit at video ended event");
test.done();
}
}, "Test the expected frames are played at the expected times, even in presence of reappends");
</script>
</body>
</html>

View file

@ -0,0 +1,146 @@
<!DOCTYPE html>
<!-- Copyright © 2019 Igalia. -->
<html>
<head>
<title>Frame checking test for simple MSE playback.</title>
<meta name="timeout" content="long">
<meta name="charset" content="UTF-8">
<link rel="author" title="Alicia Boya García" href="mailto:aboya@igalia.com">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="mediasource-util.js"></script>
</head>
<body>
<div id="log"></div>
<canvas id="test-canvas"></canvas>
<script>
function waitForEventPromise(element, event) {
return new Promise(resolve => {
function handler(ev) {
element.removeEventListener(event, handler);
resolve(ev);
}
element.addEventListener(event, handler);
});
}
function appendBufferPromise(sourceBuffer, data) {
sourceBuffer.appendBuffer(data);
return waitForEventPromise(sourceBuffer, "update");
}
function readPixel(imageData, x, y) {
return {
r: imageData.data[4 * (y * imageData.width + x)],
g: imageData.data[1 + 4 * (y * imageData.width + x)],
b: imageData.data[2 + 4 * (y * imageData.width + x)],
a: imageData.data[3 + 4 * (y * imageData.width + x)],
};
}
function isPixelLit(pixel) {
const threshold = 200; // out of 255
return pixel.r >= threshold && pixel.g >= threshold && pixel.b >= threshold;
}
// The test video has a few gray boxes. Each box interval (1 second) a new box is lit white and a different note
// is played. This test makes sure the right number of lit boxes and the right note are played at the right time.
const totalBoxes = 7;
const boxInterval = 1; // seconds
const videoWidth = 320;
const videoHeight = 240;
const boxesY = 210;
const boxSide = 20;
const boxMargin = 20;
const allBoxesWidth = totalBoxes * boxSide + (totalBoxes - 1) * boxMargin;
const boxesX = new Array(totalBoxes).fill(undefined)
.map((_, i) => (videoWidth - allBoxesWidth) / 2 + boxSide / 2 + i * (boxSide + boxMargin));
// Sound starts playing A4 (440 Hz) and goes one chromatic note up with every box lit.
// By comparing the player position to both the amount of boxes lit and the note played we can detect A/V
// synchronization issues automatically.
const noteFrequencies = new Array(1 + totalBoxes).fill(undefined)
.map((_, i) => 440 * Math.pow(Math.pow(2, 1 / 12), i));
// We also check the first second [0, 1) where no boxes are lit, therefore we start counting at -1 to do the check
// for zero lit boxes.
let boxesLitSoFar = -1;
mediasource_test(async function (test, mediaElement, mediaSource) {
const canvas = document.getElementById("test-canvas");
const canvasCtx = canvas.getContext("2d");
canvas.width = videoWidth;
canvas.height = videoHeight;
const videoData = await (await fetch("mp4/test-boxes-video.mp4")).arrayBuffer();
const audioData = (await (await fetch("mp4/test-boxes-audio.mp4")).arrayBuffer());
const videoSb = mediaSource.addSourceBuffer('video/mp4; codecs="avc1.4d401f"');
const audioSb = mediaSource.addSourceBuffer('audio/mp4; codecs="mp4a.40.2"');
mediaElement.addEventListener('error', test.unreached_func("Unexpected event 'error'"));
mediaElement.addEventListener('ended', onEnded);
mediaElement.addEventListener('timeupdate', onTimeUpdate);
await appendBufferPromise(videoSb, videoData);
await appendBufferPromise(audioSb, audioData);
mediaSource.endOfStream();
mediaElement.play();
const audioCtx = new (window.AudioContext || window.webkitAudioContext)();
const source = audioCtx.createMediaElementSource(mediaElement);
const analyser = audioCtx.createAnalyser();
analyser.fftSize = 8192;
source.connect(analyser);
analyser.connect(audioCtx.destination);
const freqDomainArray = new Float32Array(analyser.frequencyBinCount);
function checkNoteBeingPlayed() {
const expectedNoteFrequency = noteFrequencies[boxesLitSoFar];
analyser.getFloatFrequencyData(freqDomainArray);
const maxBin = freqDomainArray.reduce((prev, curValue, i) =>
curValue > prev.value ? {index: i, value: curValue} : prev,
{index: -1, value: -Infinity});
const binFrequencyWidth = audioCtx.sampleRate / analyser.fftSize;
const binFreq = maxBin.index * binFrequencyWidth;
assert_true(Math.abs(expectedNoteFrequency - binFreq) <= binFrequencyWidth,
`The note being played matches the expected one (boxes lit: ${boxesLitSoFar}, ${expectedNoteFrequency.toFixed(1)} Hz)` +
`, found ~${binFreq.toFixed(1)} Hz`);
}
function countLitBoxesInCurrentVideoFrame() {
canvasCtx.drawImage(mediaElement, 0, 0);
const imageData = canvasCtx.getImageData(0, 0, videoWidth, videoHeight);
const lights = boxesX.map(boxX => isPixelLit(readPixel(imageData, boxX, boxesY)));
let litBoxes = 0;
for (let i = 0; i < lights.length; i++) {
if (lights[i])
litBoxes++;
}
for (let i = litBoxes; i < lights.length; i++) {
assert_false(lights[i], 'After the first non-lit box, all boxes must non-lit');
}
return litBoxes;
}
function onTimeUpdate() {
const graceTime = 0.5;
if (mediaElement.currentTime >= (1 + boxesLitSoFar) * boxInterval + graceTime && boxesLitSoFar < totalBoxes) {
assert_equals(countLitBoxesInCurrentVideoFrame(), boxesLitSoFar + 1, "Num of lit boxes:");
boxesLitSoFar++;
checkNoteBeingPlayed();
}
}
function onEnded() {
assert_equals(boxesLitSoFar, totalBoxes, "Boxes lit at video ended event");
test.done();
}
}, "Test the expected frames are played at the expected times");
</script>
</body>
</html>

View file

@ -24,220 +24,28 @@
let advanced_constraints_depth = [{
videoKind: "depth",
focalLengthX: 0.5,
focalLengthY: 0.5,
principalPointX: 0.1,
principalPointY: 0.1,
deprojectionDistortionCoefficients: true,
projectionDistortionCoefficients: true,
depthNear: 0.5,
depthFar: 1,
depthToVideoTransform: true
}];
let advanced_constraints_color = [{
videoKind: "color",
focalLengthX: 0.5,
focalLengthY: 0.5,
principalPointX: 0.1,
principalPointY: 0.1,
deprojectionDistortionCoefficients: true,
projectionDistortionCoefficients: true
}];
/*
partial dictionary MediaTrackCapabilities {
// Apply to both depth stream track and color stream track:
DOMString videoKind;
(double or DoubleRange) focalLengthX;
(double or DoubleRange) focalLengthY;
(double or DoubleRange) principalPointX;
(double or DoubleRange) principalPointY;
boolean deprojectionDistortionCoefficients;
boolean projectionDistortionCoefficients;
// Apply to depth stream track:
(double or DoubleRange) depthNear;
(double or DoubleRange) depthFar;
boolean depthToVideoTransform;
};
dictionary DoubleRange {
double max;
double min;
};
*/
}];
function validateMediaTrackCapabilities(capabilities, type) {
assert_string_field(capabilities, 'videoKind');
assert_number_or_number_range_field(capabilities, 'focalLengthX');
assert_number_or_number_range_field(capabilities, 'focalLengthY');
assert_number_or_number_range_field(capabilities, 'principalPointX');
assert_number_or_number_range_field(capabilities, 'principalPointY');
assert_boolean_field(capabilities, 'deprojectionDistortionCoefficients');
assert_boolean_field(capabilities, 'projectionDistortionCoefficients');
if (type == "depth") {
assert_number_or_number_range_field(capabilities, 'depthNear');
assert_number_or_number_range_field(capabilities, 'depthFar');
assert_boolean_field(capabilities, 'depthToVideoTransform');
}
}
/*
partial dictionary MediaTrackConstraintSet {
// Apply to both depth stream track and color stream track:
ConstrainDOMString videoKind;
ConstrainDouble focalLengthX;
ConstrainDouble focalLengthY;
ConstrainDouble principalPointX;
ConstrainDouble principalPointY;
ConstrainBoolean deprojectionDistortionCoefficients;
ConstrainBoolean projectionDistortionCoefficients;
// Apply to depth stream track:
ConstrainDouble depthNear;
ConstrainDouble depthFar;
ConstrainBoolean depthToVideoTransform;
};
typedef (DOMString or sequence<DOMString> or ConstrainDOMStringParameters) ConstrainDOMString;
dictionary ConstrainDOMStringParameters {
(DOMString or sequence<DOMString>) exact;
(DOMString or sequence<DOMString>) ideal;
};
typedef (double or ConstrainDoubleRange) ConstrainDouble;
dictionary DoubleRange {
double max;
double min;
};
dictionary ConstrainDoubleRange : DoubleRange {
double exact;
double ideal;
};
typedef (boolean or ConstrainBooleanParameters) ConstrainBoolean;
dictionary ConstrainBooleanParameters {
boolean exact;
boolean ideal;
};
*/
function validateMediaTrackConstraintSet(constraints, type) {
assert_constrain_string_field(constraints, 'videoKind');
assert_constrain_number_field(constraints, 'focalLengthX');
assert_constrain_number_field(constraints, 'focalLengthY');
assert_constrain_number_field(constraints, 'principalPointX');
assert_constrain_number_field(constraints, 'principalPointY');
assert_constrain_boolean_field(constraints, 'deprojectionDistortionCoefficients');
assert_constrain_boolean_field(constraints, 'projectionDistortionCoefficients');
if (type == "depth") {
assert_constrain_number_field(constraints, 'depthNear');
assert_constrain_number_field(constraints, 'depthFar');
assert_constrain_boolean_field(constraints, 'depthToVideoTransform');
}
}
/*
partial dictionary MediaTrackSettings {
// Apply to both depth stream track and color stream track:
DOMString videoKind;
double focalLengthX;
double focalLengthY;
double principalPointX;
double principalPointY;
DistortionCoefficients deprojectionDistortionCoefficients;
DistortionCoefficients projectionDistortionCoefficients;
// Apply to depth stream track:
double depthNear;
double depthFar;
Transformation depthToVideoTransform;
};
dictionary DistortionCoefficients {
double k1;
double k2;
double p1;
double p2;
double k3;
};
dictionary Transformation {
Float32Array transformationMatrix;
DOMString videoDeviceId;
};
enum VideoKindEnum {
"color",
"depth"
};
*/
function validateDistortionCoefficients(coefficients) {
assert_number_field(coefficients, 'k1');
assert_number_field(coefficients, 'k2');
assert_number_field(coefficients, 'p1');
assert_number_field(coefficients, 'p2');
assert_number_field(coefficients, 'k3');
}
function validateTransformation(depthToVideoTransform) {
assert_array_field(depthToVideoTransform, 'transformationMatrix');
assert_string_field(depthToVideoTransform, 'videoDeviceId');
}
function validateMediaTrackSettings(settings, type) {
assert_string_field(settings, 'videoKind');
assert_enum_field(settings, 'videoKind', ['color', 'depth'])
assert_number_field(settings, 'focalLengthX');
assert_number_field(settings, 'focalLengthY');
assert_number_field(settings, 'principalPointX');
assert_number_field(settings, 'principalPointY');
if (settings.deprojectionDistortionCoefficients) {
validateDistortionCoefficients(settings.deprojectionDistortionCoefficients);
}
if (settings.projectionDistortionCoefficients) {
validateDistortionCoefficients(settings.projectionDistortionCoefficients);
}
if (type == "depth") {
assert_number_field(settings, 'depthNear');
assert_number_field(settings, 'depthFar');
if (settings.depthToVideoTransform) {
validateTransformation(settings.depthToVideoTransform);
}
}
}
/*
partial dictionary MediaTrackSupportedConstraints {
// Apply to both depth stream track and color stream track:
boolean videoKind = true;
boolean focalLengthX = false;
boolean focalLengthY = false;
boolean principalPointX = false;
boolean principalPointY = false;
boolean deprojectionDistortionCoefficients = false;
boolean projectionDistortionCoefficients = false;
// Apply to depth stream track:
boolean depthNear = false;
boolean depthFar = false;
boolean depthToVideoTransform = false;
};
*/
function validateMediaTrackSupportedConstraints(supports) {
assert_boolean_field(supports, 'videoKind', true);
assert_boolean_field(supports, 'focalLengthX', false);
assert_boolean_field(supports, 'focalLengthY', false);
assert_boolean_field(supports, 'principalPointX', false);
assert_boolean_field(supports, 'principalPointY', false);
assert_boolean_field(supports, 'deprojectionDistortionCoefficients', false);
assert_boolean_field(supports, 'projectionDistortionCoefficients', false);
assert_boolean_field(supports, 'depthNear', false);
assert_boolean_field(supports, 'depthFar', false);
assert_boolean_field(supports, 'depthToVideoTransform', false);
}
function runDictionaryTests(type, constraints) {

Some files were not shown because too many files have changed in this diff Show more