зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1429043 - Update web-platform-tests to revision 4de5305adf3d33badc23952672bcf28168fea37e, a=testonly
MozReview-Commit-ID: 8hFZiSUb5Qr --HG-- rename : testing/web-platform/tests/bluetooth/idl-Bluetooth.html => testing/web-platform/tests/bluetooth/idl/idl-Bluetooth.html rename : testing/web-platform/tests/cookies/secure/set-from-ws.https.sub.html => testing/web-platform/tests/cookies/secure/set-from-ws.sub.html rename : testing/web-platform/tests/css/css-values/iframe/vh-support-transform-origin-iframe.html => testing/web-platform/tests/css/css-values/support/vh-support-transform-origin-iframe.html rename : testing/web-platform/tests/css/css-values/iframe/vh-support-transform-translate-iframe.html => testing/web-platform/tests/css/css-values/support/vh-support-transform-translate-iframe.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/a-ref.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/a-ref.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/a.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/match/a-ref.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/match/a-ref.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/match/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/match/a.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/match/support/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/match/support/a.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/match/support/tools/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/match/support/tools/a.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/match/tools/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/match/tools/a.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/not-match/a-ref.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/not-match/a-ref.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/not-match/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/not-match/a.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/not-match/support/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/not-match/support/a.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/not-match/tools/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/not-match/tools/a.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/selectors/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/selectors/a.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/support/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/support/a.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/support/tools/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/support/tools/a.html rename : testing/web-platform/tests/tools/lint/tests/dummy/css-unique/tools/a.html => testing/web-platform/tests/tools/lint/tests/dummy/css/css-unique/tools/a.html rename : testing/web-platform/tests/tools/py/.hgignore => testing/web-platform/tests/tools/third_party/py/.hgignore rename : testing/web-platform/tests/tools/py/AUTHORS => testing/web-platform/tests/tools/third_party/py/AUTHORS rename : testing/web-platform/tests/tools/py/LICENSE => testing/web-platform/tests/tools/third_party/py/LICENSE rename : testing/web-platform/tests/tools/py/bench/localpath.py => testing/web-platform/tests/tools/third_party/py/bench/localpath.py rename : testing/web-platform/tests/tools/py/doc/Makefile => testing/web-platform/tests/tools/third_party/py/doc/Makefile rename : testing/web-platform/tests/tools/py/doc/_templates/layout.html => testing/web-platform/tests/tools/third_party/py/doc/_templates/layout.html rename : testing/web-platform/tests/tools/py/doc/announce/release-0.9.0.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-0.9.0.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-0.9.2.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-0.9.2.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.0.0.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.0.0.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.0.1.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.0.1.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.0.2.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.0.2.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.1.0.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.1.0.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.1.1.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.1.1.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.2.0.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.2.0.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.2.1.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.2.1.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.3.0.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.3.0.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.3.1.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.3.1.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.3.2.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.3.2.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.3.3.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.3.3.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.3.4.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.3.4.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.4.0.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.4.0.txt rename : testing/web-platform/tests/tools/py/doc/announce/release-1.4.1.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/release-1.4.1.txt rename : testing/web-platform/tests/tools/py/doc/announce/releases.txt => testing/web-platform/tests/tools/third_party/py/doc/announce/releases.txt rename : testing/web-platform/tests/tools/py/doc/changelog.txt => testing/web-platform/tests/tools/third_party/py/doc/changelog.txt rename : testing/web-platform/tests/tools/py/doc/code.txt => testing/web-platform/tests/tools/third_party/py/doc/code.txt rename : testing/web-platform/tests/tools/py/doc/conf.py => testing/web-platform/tests/tools/third_party/py/doc/conf.py rename : testing/web-platform/tests/tools/py/doc/download.html => testing/web-platform/tests/tools/third_party/py/doc/download.html rename : testing/web-platform/tests/tools/py/doc/example/genhtml.py => testing/web-platform/tests/tools/third_party/py/doc/example/genhtml.py rename : testing/web-platform/tests/tools/py/doc/example/genhtmlcss.py => testing/web-platform/tests/tools/third_party/py/doc/example/genhtmlcss.py rename : testing/web-platform/tests/tools/py/doc/example/genxml.py => testing/web-platform/tests/tools/third_party/py/doc/example/genxml.py rename : testing/web-platform/tests/tools/py/doc/faq.txt => testing/web-platform/tests/tools/third_party/py/doc/faq.txt rename : testing/web-platform/tests/tools/py/doc/img/pylib.png => testing/web-platform/tests/tools/third_party/py/doc/img/pylib.png rename : testing/web-platform/tests/tools/py/doc/io.txt => testing/web-platform/tests/tools/third_party/py/doc/io.txt rename : testing/web-platform/tests/tools/py/doc/links.inc => testing/web-platform/tests/tools/third_party/py/doc/links.inc rename : testing/web-platform/tests/tools/py/doc/log.txt => testing/web-platform/tests/tools/third_party/py/doc/log.txt rename : testing/web-platform/tests/tools/py/doc/style.css => testing/web-platform/tests/tools/third_party/py/doc/style.css rename : testing/web-platform/tests/tools/py/doc/xml.txt => testing/web-platform/tests/tools/third_party/py/doc/xml.txt rename : testing/web-platform/tests/tools/py/py/__metainfo.py => testing/web-platform/tests/tools/third_party/py/py/__metainfo.py rename : testing/web-platform/tests/tools/py/py/_builtin.py => testing/web-platform/tests/tools/third_party/py/py/_builtin.py rename : testing/web-platform/tests/tools/py/py/_code/__init__.py => testing/web-platform/tests/tools/third_party/py/py/_code/__init__.py rename : testing/web-platform/tests/tools/py/py/_code/_py2traceback.py => testing/web-platform/tests/tools/third_party/py/py/_code/_py2traceback.py rename : testing/web-platform/tests/tools/py/py/_io/__init__.py => testing/web-platform/tests/tools/third_party/py/py/_io/__init__.py rename : testing/web-platform/tests/tools/py/py/_io/capture.py => testing/web-platform/tests/tools/third_party/py/py/_io/capture.py rename : testing/web-platform/tests/tools/py/py/_io/saferepr.py => testing/web-platform/tests/tools/third_party/py/py/_io/saferepr.py rename : testing/web-platform/tests/tools/py/py/_log/__init__.py => testing/web-platform/tests/tools/third_party/py/py/_log/__init__.py rename : testing/web-platform/tests/tools/py/py/_path/__init__.py => testing/web-platform/tests/tools/third_party/py/py/_path/__init__.py rename : testing/web-platform/tests/tools/py/py/_path/cacheutil.py => testing/web-platform/tests/tools/third_party/py/py/_path/cacheutil.py rename : testing/web-platform/tests/tools/py/py/_process/__init__.py => testing/web-platform/tests/tools/third_party/py/py/_process/__init__.py rename : testing/web-platform/tests/tools/py/py/_process/cmdexec.py => testing/web-platform/tests/tools/third_party/py/py/_process/cmdexec.py rename : testing/web-platform/tests/tools/py/py/_process/forkedfunc.py => testing/web-platform/tests/tools/third_party/py/py/_process/forkedfunc.py rename : testing/web-platform/tests/tools/py/py/_process/killproc.py => testing/web-platform/tests/tools/third_party/py/py/_process/killproc.py rename : testing/web-platform/tests/tools/pytest/_pytest/vendored_packages/pluggy-0.3.1.dist-info/WHEEL => testing/web-platform/tests/tools/third_party/py/py/_vendored_packages/apipkg-1.4.dist-info/WHEEL rename : testing/web-platform/tests/tools/py/py/test.py => testing/web-platform/tests/tools/third_party/py/py/test.py rename : testing/web-platform/tests/tools/py/testing/code/test_code.py => testing/web-platform/tests/tools/third_party/py/testing/code/test_code.py rename : testing/web-platform/tests/tools/py/testing/conftest.py => testing/web-platform/tests/tools/third_party/py/testing/conftest.py rename : testing/web-platform/tests/tools/py/testing/io_/__init__.py => testing/web-platform/tests/tools/third_party/py/testing/io_/__init__.py rename : testing/web-platform/tests/tools/py/testing/path/repotest.dump => testing/web-platform/tests/tools/third_party/py/testing/path/repotest.dump rename : testing/web-platform/tests/tools/py/testing/path/svntestbase.py => testing/web-platform/tests/tools/third_party/py/testing/path/svntestbase.py rename : testing/web-platform/tests/tools/py/testing/path/test_svnurl.py => testing/web-platform/tests/tools/third_party/py/testing/path/test_svnurl.py rename : testing/web-platform/tests/tools/py/testing/process/__init__.py => testing/web-platform/tests/tools/third_party/py/testing/process/__init__.py rename : testing/web-platform/tests/tools/py/testing/root/__init__.py => testing/web-platform/tests/tools/third_party/py/testing/root/__init__.py rename : testing/web-platform/tests/tools/py/testing/root/test_builtin.py => testing/web-platform/tests/tools/third_party/py/testing/root/test_builtin.py rename : testing/web-platform/tests/tools/py/testing/root/test_std.py => testing/web-platform/tests/tools/third_party/py/testing/root/test_std.py rename : testing/web-platform/tests/tools/pytest/.gitattributes => testing/web-platform/tests/tools/third_party/pytest/.gitattributes rename : testing/web-platform/tests/tools/pytest/bench/bench.py => testing/web-platform/tests/tools/third_party/pytest/bench/bench.py rename : testing/web-platform/tests/tools/pytest/bench/bench_argcomplete.py => testing/web-platform/tests/tools/third_party/pytest/bench/bench_argcomplete.py rename : testing/web-platform/tests/tools/pytest/bench/empty.py => testing/web-platform/tests/tools/third_party/pytest/bench/empty.py rename : testing/web-platform/tests/tools/pytest/bench/manyparam.py => testing/web-platform/tests/tools/third_party/pytest/bench/manyparam.py rename : testing/web-platform/tests/tools/pytest/bench/skip.py => testing/web-platform/tests/tools/third_party/pytest/bench/skip.py rename : testing/web-platform/tests/tools/pytest/doc/en/_templates/sidebarintro.html => testing/web-platform/tests/tools/third_party/pytest/doc/en/_templates/sidebarintro.html rename : testing/web-platform/tests/tools/pytest/doc/en/_themes/.gitignore => testing/web-platform/tests/tools/third_party/pytest/doc/en/_themes/.gitignore rename : testing/web-platform/tests/tools/pytest/doc/en/_themes/LICENSE => testing/web-platform/tests/tools/third_party/pytest/doc/en/_themes/LICENSE rename : testing/web-platform/tests/tools/pytest/doc/en/_themes/README => testing/web-platform/tests/tools/third_party/pytest/doc/en/_themes/README rename : testing/web-platform/tests/tools/pytest/doc/en/_themes/flask/layout.html => testing/web-platform/tests/tools/third_party/pytest/doc/en/_themes/flask/layout.html rename : testing/web-platform/tests/tools/pytest/doc/en/_themes/flask/relations.html => testing/web-platform/tests/tools/third_party/pytest/doc/en/_themes/flask/relations.html rename : testing/web-platform/tests/tools/pytest/doc/en/_themes/flask/static/flasky.css_t => testing/web-platform/tests/tools/third_party/pytest/doc/en/_themes/flask/static/flasky.css_t rename : testing/web-platform/tests/tools/pytest/doc/en/_themes/flask/theme.conf => testing/web-platform/tests/tools/third_party/pytest/doc/en/_themes/flask/theme.conf rename : testing/web-platform/tests/tools/pytest/doc/en/_themes/flask_theme_support.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/_themes/flask_theme_support.py rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.0.0.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.0.0.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.0.1.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.0.1.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.1.0.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.1.0.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.1.1.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.1.1.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.1.2.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.1.2.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.1.3.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.1.3.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.2.0.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.2.0.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.2.2.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.2.2.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.3.1.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.3.1.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.3.4.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.3.4.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.4.1.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.4.1.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.4.2.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.4.2.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.5.1.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.5.1.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.6.0.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.6.0.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.6.1.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.6.1.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.6.2.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.6.2.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.7.2.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.7.2.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.8.2.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.8.2.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.8.3.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.8.3.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.8.4.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.8.4.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.8.5.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.8.5.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.8.6.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.8.6.rst rename : testing/web-platform/tests/tools/pytest/doc/en/announce/release-2.8.7.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/announce/release-2.8.7.rst rename : testing/web-platform/tests/tools/pytest/doc/en/changelog.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/changelog.rst rename : testing/web-platform/tests/tools/pytest/doc/en/check_sphinx.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/check_sphinx.py rename : testing/web-platform/tests/tools/pytest/doc/en/conftest.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/conftest.py rename : testing/web-platform/tests/tools/pytest/doc/en/contributing.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/contributing.rst rename : testing/web-platform/tests/tools/pytest/doc/en/example/assertion/global_testmodule_config/conftest.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/assertion/global_testmodule_config/conftest.py rename : testing/web-platform/tests/tools/pytest/doc/en/example/assertion/global_testmodule_config/test_hello.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/assertion/global_testmodule_config/test_hello.py rename : testing/web-platform/tests/tools/pytest/doc/en/example/assertion/test_failures.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/assertion/test_failures.py rename : testing/web-platform/tests/tools/pytest/doc/en/example/conftest.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/conftest.py rename : testing/web-platform/tests/tools/pytest/doc/en/example/costlysetup/sub1/__init__.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/costlysetup/sub1/__init__.py rename : testing/web-platform/tests/tools/pytest/doc/en/example/costlysetup/sub1/test_quick.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/costlysetup/sub1/test_quick.py rename : testing/web-platform/tests/tools/pytest/doc/en/example/costlysetup/sub2/__init__.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/costlysetup/sub2/__init__.py rename : testing/web-platform/tests/tools/pytest/doc/en/example/costlysetup/sub2/test_two.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/costlysetup/sub2/test_two.py rename : testing/web-platform/tests/tools/pytest/doc/en/example/nonpython/test_simple.yml => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/nonpython/test_simple.yml rename : testing/web-platform/tests/tools/pytest/doc/en/example/py2py3/conftest.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/py2py3/conftest.py rename : testing/web-platform/tests/tools/pytest/doc/en/example/py2py3/test_py2.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/py2py3/test_py2.py rename : testing/web-platform/tests/tools/pytest/doc/en/example/py2py3/test_py3.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/py2py3/test_py3.py rename : testing/web-platform/tests/tools/pytest/doc/en/example/xfail_demo.py => testing/web-platform/tests/tools/third_party/pytest/doc/en/example/xfail_demo.py rename : testing/web-platform/tests/tools/pytest/doc/en/funcargs.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/funcargs.rst rename : testing/web-platform/tests/tools/pytest/doc/en/img/cramer2.png => testing/web-platform/tests/tools/third_party/pytest/doc/en/img/cramer2.png rename : testing/web-platform/tests/tools/pytest/doc/en/img/freiburg2.jpg => testing/web-platform/tests/tools/third_party/pytest/doc/en/img/freiburg2.jpg rename : testing/web-platform/tests/tools/pytest/doc/en/img/gaynor3.png => testing/web-platform/tests/tools/third_party/pytest/doc/en/img/gaynor3.png rename : testing/web-platform/tests/tools/pytest/doc/en/img/keleshev.png => testing/web-platform/tests/tools/third_party/pytest/doc/en/img/keleshev.png rename : testing/web-platform/tests/tools/pytest/doc/en/img/pullrequest.png => testing/web-platform/tests/tools/third_party/pytest/doc/en/img/pullrequest.png rename : testing/web-platform/tests/tools/pytest/doc/en/img/pylib.png => testing/web-platform/tests/tools/third_party/pytest/doc/en/img/pylib.png rename : testing/web-platform/tests/tools/pytest/doc/en/img/pytest1.png => testing/web-platform/tests/tools/third_party/pytest/doc/en/img/pytest1.png rename : testing/web-platform/tests/tools/pytest/doc/en/img/pytest1favi.ico => testing/web-platform/tests/tools/third_party/pytest/doc/en/img/pytest1favi.ico rename : testing/web-platform/tests/tools/pytest/doc/en/img/theuni.png => testing/web-platform/tests/tools/third_party/pytest/doc/en/img/theuni.png rename : testing/web-platform/tests/tools/pytest/doc/en/naming20.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/naming20.rst rename : testing/web-platform/tests/tools/pytest/doc/en/pytest.ini => testing/web-platform/tests/tools/third_party/pytest/doc/en/pytest.ini rename : testing/web-platform/tests/tools/pytest/doc/en/test/config.html => testing/web-platform/tests/tools/third_party/pytest/doc/en/test/config.html rename : testing/web-platform/tests/tools/pytest/doc/en/test/dist.html => testing/web-platform/tests/tools/third_party/pytest/doc/en/test/dist.html rename : testing/web-platform/tests/tools/pytest/doc/en/test/extend.html => testing/web-platform/tests/tools/third_party/pytest/doc/en/test/extend.html rename : testing/web-platform/tests/tools/pytest/doc/en/test/index.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/test/index.rst rename : testing/web-platform/tests/tools/pytest/doc/en/test/plugin/django.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/test/plugin/django.rst rename : testing/web-platform/tests/tools/pytest/doc/en/test/plugin/index.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/test/plugin/index.rst rename : testing/web-platform/tests/tools/pytest/doc/en/test/plugin/oejskit.rst => testing/web-platform/tests/tools/third_party/pytest/doc/en/test/plugin/oejskit.rst rename : testing/web-platform/tests/tools/pytest/doc/en/test/test.html => testing/web-platform/tests/tools/third_party/pytest/doc/en/test/test.html rename : testing/web-platform/tests/tools/pytest/extra/setup-py.test/setup.py => testing/web-platform/tests/tools/third_party/pytest/extra/setup-py.test/setup.py rename : testing/web-platform/tests/tools/pytest/testing/cx_freeze/tests/test_doctest.txt => testing/web-platform/tests/tools/third_party/pytest/testing/freeze/tests/test_doctest.txt
This commit is contained in:
Родитель
3a7a79b6b8
Коммит
0375220b76
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -1,2 +1,2 @@
|
|||
local: 5b33b070378ae0806bed0b5e5e34de429a29e7db
|
||||
upstream: df8508402c2b47f43c2f5bf8da8ba9e95a2a56b9
|
||||
local: ac93fdadf1022211eec62258ad22b42cb37a6d14
|
||||
upstream: 45668a155fde7eb87c4c82721fc18fcf8c757c60
|
||||
|
|
|
@ -63,7 +63,7 @@ matrix:
|
|||
- fonts-liberation
|
||||
env:
|
||||
- secure: "YTSXPwI0DyCA1GhYrLT9KMEV6b7QQKuEeaQgeFDP38OTzJ1+cIj3CC4SRNqbnJ/6SJwPGcdqSxLuV8m4e5HFFnyCcQnJe6h8EMsTehZ7W3j/fP9UYrJqYqvGpe3Vj3xblO5pwBYmq7sg3jAmmuCgAgOW6VGf7cRMucrsmFeo7VM="
|
||||
- JOB=stability SCRIPT=tools/ci/ci_stability.sh PRODUCT=chrome:unstable
|
||||
- JOB=stability SCRIPT=tools/ci/ci_stability.sh PRODUCT=chrome:dev
|
||||
- os: linux
|
||||
python: "2.7"
|
||||
env:
|
||||
|
@ -94,7 +94,7 @@ matrix:
|
|||
- env: JOB=build_css SCRIPT=css/build-css-testsuites.sh
|
||||
- env:
|
||||
- secure: "YTSXPwI0DyCA1GhYrLT9KMEV6b7QQKuEeaQgeFDP38OTzJ1+cIj3CC4SRNqbnJ/6SJwPGcdqSxLuV8m4e5HFFnyCcQnJe6h8EMsTehZ7W3j/fP9UYrJqYqvGpe3Vj3xblO5pwBYmq7sg3jAmmuCgAgOW6VGf7cRMucrsmFeo7VM="
|
||||
- JOB=stability SCRIPT=tools/ci/ci_stability.sh PRODUCT=chrome:unstable
|
||||
- JOB=stability SCRIPT=tools/ci/ci_stability.sh PRODUCT=chrome:dev
|
||||
- env:
|
||||
- secure: "YTSXPwI0DyCA1GhYrLT9KMEV6b7QQKuEeaQgeFDP38OTzJ1+cIj3CC4SRNqbnJ/6SJwPGcdqSxLuV8m4e5HFFnyCcQnJe6h8EMsTehZ7W3j/fP9UYrJqYqvGpe3Vj3xblO5pwBYmq7sg3jAmmuCgAgOW6VGf7cRMucrsmFeo7VM="
|
||||
- JOB=stability SCRIPT=tools/ci/ci_stability.sh PRODUCT=sauce:MicrosoftEdge:14.14393 PLATFORM='Windows 10'
|
||||
|
|
|
@ -21,6 +21,15 @@ _addTest(function(canvas, ctx) {
|
|||
|
||||
var g = ctx.createLinearGradient(0, 0, 100, 0);
|
||||
assert_throws("SYNTAX_ERR", function() { g.addColorStop(0, ""); });
|
||||
assert_throws("SYNTAX_ERR", function() { g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)'); });
|
||||
assert_throws("SYNTAX_ERR", function() { g.addColorStop(0, 'null'); });
|
||||
assert_throws("SYNTAX_ERR", function() { g.addColorStop(0, 'undefined'); });
|
||||
assert_throws("SYNTAX_ERR", function() { g.addColorStop(0, null); });
|
||||
assert_throws("SYNTAX_ERR", function() { g.addColorStop(0, undefined); });
|
||||
|
||||
var g = ctx.createRadialGradient(0, 0, 0, 100, 0, 0);
|
||||
assert_throws("SYNTAX_ERR", function() { g.addColorStop(0, ""); });
|
||||
assert_throws("SYNTAX_ERR", function() { g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)'); });
|
||||
assert_throws("SYNTAX_ERR", function() { g.addColorStop(0, 'null'); });
|
||||
assert_throws("SYNTAX_ERR", function() { g.addColorStop(0, 'undefined'); });
|
||||
assert_throws("SYNTAX_ERR", function() { g.addColorStop(0, null); });
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html>
|
||||
<head>
|
||||
<!doctype html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
function makeCanvas() {
|
||||
|
@ -86,6 +82,22 @@ function makeInvalidBlob() {
|
|||
});
|
||||
}
|
||||
|
||||
function makeBrokenImage() {
|
||||
return new Promise(resolve => {
|
||||
const image = new Image();
|
||||
image.src = "data,x";
|
||||
image.onerror = () => resolve(image);
|
||||
});
|
||||
}
|
||||
|
||||
function makeAvailableButBrokenImage() {
|
||||
return new Promise(resolve => {
|
||||
const image = new Image();
|
||||
image.src = "/images/broken.png";
|
||||
image.onload = () => resolve(image);
|
||||
});
|
||||
}
|
||||
|
||||
imageSourceTypes = [
|
||||
{ name: 'HTMLImageElement', factory: makeImage },
|
||||
{ name: 'HTMLVideoElement', factory: makeVideo },
|
||||
|
@ -152,36 +164,48 @@ promise_test( t => {
|
|||
}, "createImageBitmap with null image source rejects with a TypeError.");
|
||||
|
||||
promise_test( t => {
|
||||
return promise_rejects(t, new DOMException('', 'InvalidStateError'),
|
||||
return promise_rejects(t, "InvalidStateError",
|
||||
createImageBitmap(new Image()));
|
||||
}, "createImageBitmap with empty image source rejects with a InvalidStateError.");
|
||||
|
||||
promise_test( t => {
|
||||
return promise_rejects(t, new DOMException('', 'InvalidStateError'),
|
||||
return promise_rejects(t, "InvalidStateError",
|
||||
createImageBitmap(document.createElement('video')));
|
||||
}, "createImageBitmap with empty video source rejects with a InvalidStateError.");
|
||||
|
||||
promise_test( t => {
|
||||
return makeOversizedCanvas().then(canvas => {
|
||||
return promise_rejects(t, new DOMException('', 'InvalidStateError'),
|
||||
return promise_rejects(t, "InvalidStateError",
|
||||
createImageBitmap(canvas));
|
||||
});
|
||||
}, "createImageBitmap with an oversized canvas source rejects with a RangeError.");
|
||||
|
||||
promise_test( t => {
|
||||
return makeOversizedOffscreenCanvas().then(offscreenCanvas => {
|
||||
return promise_rejects(t, new DOMException('', 'InvalidStateError'),
|
||||
return promise_rejects(t, "InvalidStateError",
|
||||
createImageBitmap(offscreenCanvas));
|
||||
});
|
||||
}, "createImageBitmap with an invalid OffscreenCanvas source rejects with a RangeError.");
|
||||
|
||||
promise_test( t => {
|
||||
return makeInvalidBlob().then(blob => {
|
||||
return promise_rejects(t, new DOMException('', 'InvalidStateError'),
|
||||
return promise_rejects(t, "InvalidStateError",
|
||||
createImageBitmap(blob));
|
||||
});
|
||||
}, "createImageBitmap with an undecodable blob source rejects with an InvalidStateError.");
|
||||
|
||||
promise_test( t => {
|
||||
return makeBrokenImage().then(image => {
|
||||
return promise_rejects(t, "InvalidStateError",
|
||||
createImageBitmap(image));
|
||||
});
|
||||
}, "createImageBitmap with a broken image source rejects with an InvalidStateError.");
|
||||
|
||||
promise_test( t => {
|
||||
return makeAvailableButBrokenImage().then(image => {
|
||||
return promise_rejects(t, "InvalidStateError",
|
||||
createImageBitmap(image));
|
||||
});
|
||||
}, "createImageBitmap with an available but undecodable image source rejects with an InvalidStateError.");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -2092,6 +2092,15 @@
|
|||
code: |
|
||||
var g = ctx.createLinearGradient(0, 0, 100, 0);
|
||||
@assert throws SYNTAX_ERR g.addColorStop(0, "");
|
||||
@assert throws SYNTAX_ERR g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)');
|
||||
@assert throws SYNTAX_ERR g.addColorStop(0, 'null');
|
||||
@assert throws SYNTAX_ERR g.addColorStop(0, 'undefined');
|
||||
@assert throws SYNTAX_ERR g.addColorStop(0, null);
|
||||
@assert throws SYNTAX_ERR g.addColorStop(0, undefined);
|
||||
|
||||
var g = ctx.createRadialGradient(0, 0, 0, 100, 0, 0);
|
||||
@assert throws SYNTAX_ERR g.addColorStop(0, "");
|
||||
@assert throws SYNTAX_ERR g.addColorStop(0, 'rgb(NaN%, NaN%, NaN%)');
|
||||
@assert throws SYNTAX_ERR g.addColorStop(0, 'null');
|
||||
@assert throws SYNTAX_ERR g.addColorStop(0, 'undefined');
|
||||
@assert throws SYNTAX_ERR g.addColorStop(0, null);
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
<!doctype html>
|
||||
<title>Revoking blob URL used with Request/fetch</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
|
||||
<script>
|
||||
promise_test(function(t) {
|
||||
const blob = new Blob(["test"]);
|
||||
const url = URL.createObjectURL(blob);
|
||||
const request = new Request(url);
|
||||
|
||||
// Revoke the object URL. Request should take a reference to the blob as
|
||||
// soon as it receives it in open(), so the request succeeds even though we
|
||||
// revoke the URL before calling fetch().
|
||||
URL.revokeObjectURL(url);
|
||||
|
||||
return fetch(request).then(response => response.text()).then(text => {
|
||||
assert_equals(text, 'test');
|
||||
});
|
||||
}, "Revoke blob URL after creating Request, will fetch");
|
||||
|
||||
promise_test(function(t) {
|
||||
const blob = new Blob(["test"]);
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
return fetch(url).then(response => response.text()).then(text => {
|
||||
assert_equals(text, 'test');
|
||||
});
|
||||
|
||||
// Revoke the object URL. fetch should have already resolved the blob URL.
|
||||
URL.revokeObjectURL(url);
|
||||
}, "Revoke blob URL after fetch, will fetch");
|
||||
|
||||
promise_test(t => {
|
||||
const blob = new Blob(["test"]);
|
||||
const url = URL.createObjectURL(blob);
|
||||
URL.revokeObjectURL(url);
|
||||
const request = new Request(url);
|
||||
return promise_rejects(t, new TypeError, fetch(request));
|
||||
}, "Revoke blob URL before creating Request, network error (after fetch)")
|
||||
</script>
|
|
@ -0,0 +1,104 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>Blob constructor: endings option</title>
|
||||
<link rel=help href="https://w3c.github.io/FileAPI/#constructorBlob">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
|
||||
// Windows platforms use CRLF as the native line ending. All others use LF.
|
||||
const crlf = navigator.platform.startsWith('Win');
|
||||
const native_ending = crlf ? '\r\n' : '\n';
|
||||
|
||||
function readBlobAsPromise(blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(blob);
|
||||
reader.onload = e => resolve(reader.result);
|
||||
reader.onerror = e => reject(reader.error);
|
||||
});
|
||||
}
|
||||
|
||||
[
|
||||
'transparent',
|
||||
'native'
|
||||
].forEach(value => test(t => {
|
||||
assert_class_string(new Blob([], {endings: value}), 'Blob',
|
||||
`Constructor should allow "${value}" endings`);
|
||||
}, `Valid "endings" value: ${JSON.stringify(value)}`));
|
||||
|
||||
[
|
||||
null,
|
||||
'',
|
||||
'invalidEnumValue',
|
||||
'Transparent',
|
||||
'NATIVE',
|
||||
0,
|
||||
{}
|
||||
].forEach(value => test(t => {
|
||||
assert_throws(new TypeError(), () => new Blob([], {endings: value}),
|
||||
'Blob constructor should throw');
|
||||
}, `Invalid "endings" value: ${JSON.stringify(value)}`));
|
||||
|
||||
test(t => {
|
||||
const test_error = {name: 'test'};
|
||||
assert_throws(
|
||||
test_error,
|
||||
() => new Blob([], { get endings() { throw test_error; }}),
|
||||
'Blob constructor should propagate exceptions from "endings" property');
|
||||
}, 'Exception propagation from options');
|
||||
|
||||
test(t => {
|
||||
let got = false;
|
||||
new Blob([], { get endings() { got = true; } });
|
||||
assert_true(got, 'The "endings" property was accessed during construction.');
|
||||
}, 'The "endings" options property is used');
|
||||
|
||||
[
|
||||
{name: 'LF', input: '\n', native: native_ending},
|
||||
{name: 'CR', input: '\r', native: native_ending},
|
||||
|
||||
{name: 'CRLF', input: '\r\n', native: native_ending},
|
||||
{name: 'CRCR', input: '\r\r', native: native_ending.repeat(2)},
|
||||
{name: 'LFCR', input: '\n\r', native: native_ending.repeat(2)},
|
||||
{name: 'LFLF', input: '\n\n', native: native_ending.repeat(2)},
|
||||
|
||||
{name: 'CRCRLF', input: '\r\r\n', native: native_ending.repeat(2)},
|
||||
{name: 'CRLFLF', input: '\r\n\n', native: native_ending.repeat(2)},
|
||||
{name: 'CRLFCR', input: '\r\n\r\n', native: native_ending.repeat(2)},
|
||||
|
||||
{name: 'CRLFCRLF', input: '\r\n\r\n', native: native_ending.repeat(2)},
|
||||
{name: 'LFCRLFCR', input: '\n\r\n\r', native: native_ending.repeat(3)},
|
||||
|
||||
].forEach(testCase => {
|
||||
promise_test(async t => {
|
||||
const blob = new Blob([testCase.input]);
|
||||
assert_equals(
|
||||
await readBlobAsPromise(blob), testCase.input,
|
||||
'Newlines should not change with endings unspecified');
|
||||
}, `Input ${testCase.name} with endings unspecified`);
|
||||
|
||||
promise_test(async t => {
|
||||
const blob = new Blob([testCase.input], {endings: 'transparent'});
|
||||
assert_equals(
|
||||
await readBlobAsPromise(blob), testCase.input,
|
||||
'Newlines should not change with endings "transparent"');
|
||||
}, `Input ${testCase.name} with endings 'transparent'`);
|
||||
|
||||
promise_test(async t => {
|
||||
const blob = new Blob([testCase.input], {endings: 'native'});
|
||||
assert_equals(
|
||||
await readBlobAsPromise(blob), testCase.native,
|
||||
'Newlines should match the platform with endings "native"');
|
||||
}, `Input ${testCase.name} with endings 'native'`);
|
||||
});
|
||||
|
||||
promise_test(async t => {
|
||||
const blob = new Blob(['\r', '\n'], {endings: 'native'});
|
||||
const expected = native_ending.repeat(2);
|
||||
assert_equals(
|
||||
await readBlobAsPromise(blob), expected,
|
||||
'CR/LF in adjacent strings should be converted to two platform newlines');
|
||||
}, `CR/LF in adjacent input strings`);
|
||||
|
||||
</script>
|
|
@ -74,6 +74,20 @@ test_blob(function() {
|
|||
type: "",
|
||||
desc: "A plain object with @@iterator should be treated as a sequence for the blobParts argument."
|
||||
});
|
||||
test(t => {
|
||||
const blob = new Blob({
|
||||
[Symbol.iterator]() {
|
||||
var i = 0;
|
||||
return {next: () => [
|
||||
{done:false, value:'ab'},
|
||||
{done:false, value:'cde'},
|
||||
{done:true}
|
||||
][i++]
|
||||
};
|
||||
}
|
||||
});
|
||||
assert_equals(blob.size, 5, 'Custom @@iterator should be treated as a sequence');
|
||||
}, "A plain object with custom @@iterator should be treated as a sequence for the blobParts argument.");
|
||||
test_blob(function() {
|
||||
return new Blob({
|
||||
[Symbol.iterator]: Array.prototype[Symbol.iterator],
|
||||
|
@ -392,26 +406,20 @@ test_blob(function() {
|
|||
desc: "Array with mixed types"
|
||||
});
|
||||
|
||||
// options argument
|
||||
test(function() {
|
||||
new Blob([], { endings: "invalidEnumValue" });
|
||||
new Blob([], { endings: null });
|
||||
new Blob([], { endings: undefined });
|
||||
new Blob([], { endings: 0 });
|
||||
new Blob([], { get endings() { assert_unreached("Should not call getter"); } });
|
||||
}, "The 'endings' property should be ignored.");
|
||||
const accessed = [];
|
||||
const stringified = [];
|
||||
|
||||
test(function() {
|
||||
assert_throws(test_error, function() {
|
||||
new Blob([], {
|
||||
get type() { throw test_error; }
|
||||
});
|
||||
new Blob([], {
|
||||
get type() { accessed.push('type'); },
|
||||
get endings() { accessed.push('endings'); }
|
||||
});
|
||||
assert_throws(test_error, function() {
|
||||
new Blob([], {
|
||||
type: { toString: function() { throw test_error; } }
|
||||
});
|
||||
new Blob([], {
|
||||
type: { toString: () => { stringified.push('type'); return ''; } },
|
||||
endings: { toString: () => { stringified.push('endings'); return 'transparent'; } }
|
||||
});
|
||||
assert_array_equals(accessed, ['endings', 'type']);
|
||||
assert_array_equals(stringified, ['endings', 'type']);
|
||||
}, "options properties should be accessed in lexicographic order.");
|
||||
|
||||
test(function() {
|
||||
|
@ -449,19 +457,16 @@ test(function() {
|
|||
});
|
||||
});
|
||||
|
||||
test_blob(function() {
|
||||
return new Blob(["\na\r\nb\n\rc\r"], { endings: "transparent" });
|
||||
}, {
|
||||
expected: "\na\r\nb\n\rc\r",
|
||||
type: "",
|
||||
desc: "Newlines should not change when endings is 'transparent'."
|
||||
});
|
||||
test_blob(function() {
|
||||
return new Blob(["\na\r\nb\n\rc\r"], { endings: "native" });
|
||||
}, {
|
||||
expected: "\na\r\nb\n\rc\r",
|
||||
type: "",
|
||||
desc: "Newlines should not change when endings is 'native'."
|
||||
[
|
||||
123,
|
||||
123.4,
|
||||
true,
|
||||
'abc'
|
||||
].forEach(arg => {
|
||||
test(t => {
|
||||
assert_throws(new TypeError(), () => new Blob([], arg),
|
||||
'Blob constructor should throw with invalid property bag');
|
||||
}, `Passing ${JSON.stringify(arg)} for options should throw`);
|
||||
});
|
||||
|
||||
var type_tests = [
|
||||
|
@ -471,6 +476,7 @@ var type_tests = [
|
|||
[[], 'A', 'a'],
|
||||
[[], 'text/html', 'text/html'],
|
||||
[[], 'TEXT/HTML', 'text/html'],
|
||||
[[], 'text/plain;charset=utf-8', 'text/plain;charset=utf-8'],
|
||||
[[], '\u00E5', ''],
|
||||
[[], '\uD801\uDC7E', ''], // U+1047E
|
||||
[[], ' image/gif ', ' image/gif '],
|
||||
|
|
|
@ -0,0 +1,104 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset=utf-8>
|
||||
<title>File constructor: endings option</title>
|
||||
<link rel=help href="https://w3c.github.io/FileAPI/#file-constructor">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
|
||||
// Windows platforms use CRLF as the native line ending. All others use LF.
|
||||
const crlf = navigator.platform.startsWith('Win');
|
||||
const native_ending = crlf ? '\r\n' : '\n';
|
||||
|
||||
function readBlobAsPromise(blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(blob);
|
||||
reader.onload = e => resolve(reader.result);
|
||||
reader.onerror = e => reject(reader.error);
|
||||
});
|
||||
}
|
||||
|
||||
[
|
||||
'transparent',
|
||||
'native'
|
||||
].forEach(value => test(t => {
|
||||
assert_class_string(new File([], "name", {endings: value}), 'File',
|
||||
`Constructor should allow "${value}" endings`);
|
||||
}, `Valid "endings" value: ${JSON.stringify(value)}`));
|
||||
|
||||
[
|
||||
null,
|
||||
'',
|
||||
'invalidEnumValue',
|
||||
'Transparent',
|
||||
'NATIVE',
|
||||
0,
|
||||
{}
|
||||
].forEach(value => test(t => {
|
||||
assert_throws(new TypeError(), () => new File([], "name", {endings: value}),
|
||||
'File constructor should throw');
|
||||
}, `Invalid "endings" value: ${JSON.stringify(value)}`));
|
||||
|
||||
test(t => {
|
||||
const test_error = {name: 'test'};
|
||||
assert_throws(
|
||||
test_error,
|
||||
() => new File([], "name", { get endings() { throw test_error; }}),
|
||||
'File constructor should propagate exceptions from "endings" property');
|
||||
}, 'Exception propagation from options');
|
||||
|
||||
test(t => {
|
||||
let got = false;
|
||||
new File([], "name", { get endings() { got = true; } });
|
||||
assert_true(got, 'The "endings" property was accessed during construction.');
|
||||
}, 'The "endings" options property is used');
|
||||
|
||||
[
|
||||
{name: 'LF', input: '\n', native: native_ending},
|
||||
{name: 'CR', input: '\r', native: native_ending},
|
||||
|
||||
{name: 'CRLF', input: '\r\n', native: native_ending},
|
||||
{name: 'CRCR', input: '\r\r', native: native_ending.repeat(2)},
|
||||
{name: 'LFCR', input: '\n\r', native: native_ending.repeat(2)},
|
||||
{name: 'LFLF', input: '\n\n', native: native_ending.repeat(2)},
|
||||
|
||||
{name: 'CRCRLF', input: '\r\r\n', native: native_ending.repeat(2)},
|
||||
{name: 'CRLFLF', input: '\r\n\n', native: native_ending.repeat(2)},
|
||||
{name: 'CRLFCR', input: '\r\n\r\n', native: native_ending.repeat(2)},
|
||||
|
||||
{name: 'CRLFCRLF', input: '\r\n\r\n', native: native_ending.repeat(2)},
|
||||
{name: 'LFCRLFCR', input: '\n\r\n\r', native: native_ending.repeat(3)},
|
||||
|
||||
].forEach(testCase => {
|
||||
promise_test(async t => {
|
||||
const file = new File([testCase.input], "name");
|
||||
assert_equals(
|
||||
await readBlobAsPromise(file), testCase.input,
|
||||
'Newlines should not change with endings unspecified');
|
||||
}, `Input ${testCase.name} with endings unspecified`);
|
||||
|
||||
promise_test(async t => {
|
||||
const file = new File([testCase.input], "name", {endings: 'transparent'});
|
||||
assert_equals(
|
||||
await readBlobAsPromise(file), testCase.input,
|
||||
'Newlines should not change with endings "transparent"');
|
||||
}, `Input ${testCase.name} with endings 'transparent'`);
|
||||
|
||||
promise_test(async t => {
|
||||
const file = new File([testCase.input], "name", {endings: 'native'});
|
||||
assert_equals(
|
||||
await readBlobAsPromise(file), testCase.native,
|
||||
'Newlines should match the platform with endings "native"');
|
||||
}, `Input ${testCase.name} with endings 'native'`);
|
||||
});
|
||||
|
||||
promise_test(async t => {
|
||||
const file = new File(['\r', '\n'], "name", {endings: 'native'});
|
||||
const expected = native_ending.repeat(2);
|
||||
assert_equals(
|
||||
await readBlobAsPromise(file), expected,
|
||||
'CR/LF in adjacent strings should be converted to two platform newlines');
|
||||
}, `CR/LF in adjacent input strings`);
|
||||
|
||||
</script>
|
|
@ -6,10 +6,20 @@
|
|||
<script src="/resources/testharnessreport.js"></script>
|
||||
<div id="log"></div>
|
||||
<script>
|
||||
const to_string_obj = { toString: () => 'a string' };
|
||||
const to_string_throws = { toString: () => { throw new Error('expected'); } };
|
||||
|
||||
test(function() {
|
||||
assert_true("File" in window, "window should have a File property.");
|
||||
}, "File interface object exists");
|
||||
|
||||
test(t => {
|
||||
assert_throws(new TypeError(), () => new File(),
|
||||
'Bits argument is required');
|
||||
assert_throws(new TypeError(), () => new File([]),
|
||||
'Name argument is required');
|
||||
}, 'Required arguments');
|
||||
|
||||
function test_first_argument(arg1, expectedSize, testName) {
|
||||
test(function() {
|
||||
var file = new File(arg1, "dummy");
|
||||
|
@ -22,14 +32,48 @@ function test_first_argument(arg1, expectedSize, testName) {
|
|||
}, testName);
|
||||
}
|
||||
|
||||
test_first_argument([], 0, "empty fileBits");
|
||||
test_first_argument(["bits"], 4, "DOMString fileBits");
|
||||
test_first_argument(["𝓽𝓮𝔁𝓽"], 16, "Unicode DOMString fileBits");
|
||||
test_first_argument([new String('string object')], 13, "String object fileBits");
|
||||
test_first_argument([new Blob()], 0, "Empty Blob fileBits");
|
||||
test_first_argument([new Blob(["bits"])], 4, "Blob fileBits");
|
||||
test_first_argument([new File([], 'world.txt')], 0, "Empty File fileBits");
|
||||
test_first_argument([new File(["bits"], 'world.txt')], 4, "File fileBits");
|
||||
test_first_argument([new ArrayBuffer(8)], 8, "ArrayBuffer fileBits");
|
||||
test_first_argument([new Uint8Array([0x50, 0x41, 0x53, 0x53])], 4, "Typed array fileBits");
|
||||
test_first_argument(["bits", new Blob(["bits"]), new Blob(), new Uint8Array([0x50, 0x41]),
|
||||
new Uint16Array([0x5353]), new Uint32Array([0x53534150])], 16, "Various fileBits");
|
||||
test_first_argument([12], 2, "Number in fileBits");
|
||||
test_first_argument([[1,2,3]], 5, "Array in fileBits");
|
||||
test_first_argument([{}], 15, "Object in fileBits"); // "[object Object]"
|
||||
test_first_argument([document], 21, "HTMLDocument in fileBits"); // "[object HTMLDocument]"
|
||||
test_first_argument([to_string_obj], 8, "Object with toString in fileBits");
|
||||
test_first_argument({[Symbol.iterator]() {
|
||||
let i = 0;
|
||||
return {next: () => [
|
||||
{done:false, value:'ab'},
|
||||
{done:false, value:'cde'},
|
||||
{done:true}
|
||||
][i++]};
|
||||
}}, 5, 'Custom @@iterator');
|
||||
|
||||
[
|
||||
'hello',
|
||||
0,
|
||||
null
|
||||
].forEach(arg => {
|
||||
test(t => {
|
||||
assert_throws(new TypeError(), () => new File(arg, 'world.html'),
|
||||
'Constructor should throw for invalid bits argument');
|
||||
}, `Invalid bits argument: ${JSON.stringify(arg)}`);
|
||||
});
|
||||
|
||||
test(t => {
|
||||
assert_throws(new Error(), () => new File([to_string_throws], 'name.txt'),
|
||||
'Constructor should propagate exceptions');
|
||||
}, 'Bits argument: object that throws');
|
||||
|
||||
|
||||
function test_second_argument(arg2, expectedFileName, testName) {
|
||||
test(function() {
|
||||
|
@ -41,23 +85,29 @@ function test_second_argument(arg2, expectedFileName, testName) {
|
|||
|
||||
test_second_argument("dummy", "dummy", "Using fileName");
|
||||
test_second_argument("dummy/foo", "dummy:foo", "Using special character in fileName");
|
||||
test_second_argument(null, "null", "Using null fileName");
|
||||
test_second_argument(1, "1", "Using number fileName");
|
||||
test_second_argument('', '', "Using empty string fileName");
|
||||
test_second_argument(document, '[object HTMLDocument]', "Using object fileName");
|
||||
|
||||
// testing the third argument
|
||||
test(function() {
|
||||
var file = new File(["bits"], "dummy", { type: "text/plain"});
|
||||
assert_true(file instanceof File);
|
||||
assert_equals(file.type, "text/plain");
|
||||
}, "Using type on the File constructor");
|
||||
test(function() {
|
||||
var file = new File(["bits"], "dummy", { type: "TEXT/PLAIN"});
|
||||
assert_true(file instanceof File);
|
||||
assert_equals(file.type, "text/plain");
|
||||
}, "Using uppercase characters in type");
|
||||
test(function() {
|
||||
var file = new File(["bits"], "dummy", { type: "𝓽𝓮𝔁𝓽/𝔭𝔩𝔞𝔦𝔫"});
|
||||
assert_true(file instanceof File);
|
||||
assert_equals(file.type, "");
|
||||
}, "Using illegal character for type");
|
||||
[
|
||||
{type: 'text/plain', expected: 'text/plain'},
|
||||
{type: 'text/plain;charset=UTF-8', expected: 'text/plain;charset=utf-8'},
|
||||
{type: 'TEXT/PLAIN', expected: 'text/plain'},
|
||||
{type: '𝓽𝓮𝔁𝓽/𝔭𝔩𝔞𝔦𝔫', expected: ''},
|
||||
{type: 'ascii/nonprintable\u001F', expected: ''},
|
||||
{type: 'ascii/nonprintable\u007F', expected: ''},
|
||||
{type: 'nonascii\u00EE', expected: ''},
|
||||
{type: 'nonascii\u1234', expected: ''},
|
||||
{type: 'nonparsable', expected: 'nonparsable'}
|
||||
].forEach(testCase => {
|
||||
test(t => {
|
||||
var file = new File(["bits"], "dummy", { type: testCase.type});
|
||||
assert_true(file instanceof File);
|
||||
assert_equals(file.type, testCase.expected);
|
||||
}, `Using type in File constructor: ${testCase.type}`);
|
||||
});
|
||||
test(function() {
|
||||
var file = new File(["bits"], "dummy", { lastModified: 42 });
|
||||
assert_true(file instanceof File);
|
||||
|
@ -68,5 +118,41 @@ test(function() {
|
|||
assert_true(file instanceof File);
|
||||
assert_equals(file.name, "dummy");
|
||||
}, "Misusing name");
|
||||
test(function() {
|
||||
var file = new File(["bits"], "dummy", { unknownKey: "value" });
|
||||
assert_true(file instanceof File);
|
||||
assert_equals(file.name, "dummy");
|
||||
}, "Unknown properties are ignored");
|
||||
|
||||
[
|
||||
123,
|
||||
123.4,
|
||||
true,
|
||||
'abc'
|
||||
].forEach(arg => {
|
||||
test(t => {
|
||||
assert_throws(new TypeError(), () => new File(['bits'], 'name.txt', arg),
|
||||
'Constructor should throw for invalid property bag type');
|
||||
}, `Invalid property bag: ${JSON.stringify(arg)}`);
|
||||
});
|
||||
|
||||
[
|
||||
null,
|
||||
undefined,
|
||||
[1,2,3],
|
||||
/regex/,
|
||||
function() {}
|
||||
].forEach(arg => {
|
||||
test(t => {
|
||||
assert_equals(new File(['bits'], 'name.txt', arg).size, 4,
|
||||
'Constructor should accept object-ish property bag type');
|
||||
}, `Unusual but valid property bag: ${arg}`);
|
||||
});
|
||||
|
||||
test(t => {
|
||||
assert_throws(new Error(),
|
||||
() => new File(['bits'], 'name.txt', {type: to_string_throws}),
|
||||
'Constructor should propagate exceptions');
|
||||
}, 'Property bag propagates exceptions');
|
||||
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,71 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Upload files in ISO-2022-JP form (tentative)</title>
|
||||
<!--
|
||||
NOTE: This test is tentative because encoding for filename
|
||||
characters unrepresentable in the form charset is not yet
|
||||
standardized.
|
||||
-->
|
||||
<link rel="help"
|
||||
href="https://github.com/whatwg/html/issues/3223">
|
||||
<link rel="help"
|
||||
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data">
|
||||
<link rel="help"
|
||||
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist">
|
||||
<link rel="help"
|
||||
href="https://w3c.github.io/FileAPI/#file-constructor">
|
||||
<link rel="author" title="Benjamin C. Wiley Sittler"
|
||||
href="mailto:bsittler@chromium.org">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../support/send-file-form-helper.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'ASCII',
|
||||
fileBaseName: 'file-for-upload-in-form.txt',
|
||||
formEncoding: 'ISO-2022-JP',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'x-user-defined',
|
||||
fileBaseName: 'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt',
|
||||
formEncoding: 'ISO-2022-JP',
|
||||
expectedEncodedBaseName: (
|
||||
'file-for-upload-in-form-.txt'),
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'windows-1252',
|
||||
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
formEncoding: 'ISO-2022-JP',
|
||||
expectedEncodedBaseName: (
|
||||
'file-for-upload-in-form-☺😂.txt'),
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'JIS X 0201 and JIS X 0208',
|
||||
fileBaseName: 'file-for-upload-in-form-★星★.txt',
|
||||
formEncoding: 'ISO-2022-JP',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form-\x1B$B!z@1!z\x1B(B.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'Unicode',
|
||||
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
formEncoding: 'ISO-2022-JP',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'Unicode',
|
||||
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
|
||||
formEncoding: 'ISO-2022-JP',
|
||||
expectedEncodedBaseName: `file-for-upload-in-form-${
|
||||
kTestFallbackIso2022jp
|
||||
}.txt`,
|
||||
});
|
||||
|
||||
</script>
|
|
@ -0,0 +1,61 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Upload files in UTF-8 form</title>
|
||||
<link rel="help"
|
||||
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data">
|
||||
<link rel="help"
|
||||
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist">
|
||||
<link rel="help"
|
||||
href="https://w3c.github.io/FileAPI/#file-constructor">
|
||||
<link rel="author" title="Benjamin C. Wiley Sittler"
|
||||
href="mailto:bsittler@chromium.org">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../support/send-file-form-helper.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'ASCII',
|
||||
fileBaseName: 'file-for-upload-in-form.txt',
|
||||
formEncoding: 'UTF-8',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'x-user-defined',
|
||||
fileBaseName: 'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt',
|
||||
formEncoding: 'UTF-8',
|
||||
expectedEncodedBaseName: (
|
||||
'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt'),
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'windows-1252',
|
||||
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
formEncoding: 'UTF-8',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'JIS X 0201 and JIS X 0208',
|
||||
fileBaseName: 'file-for-upload-in-form-★星★.txt',
|
||||
formEncoding: 'UTF-8',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form-★星★.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'Unicode',
|
||||
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
formEncoding: 'UTF-8',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'Unicode',
|
||||
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
|
||||
formEncoding: 'UTF-8',
|
||||
expectedEncodedBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
|
||||
});
|
||||
|
||||
</script>
|
|
@ -0,0 +1,70 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Upload files in Windows-1252 form (tentative)</title>
|
||||
<!--
|
||||
NOTE: This test is tentative because encoding for filename
|
||||
characters unrepresentable in the form charset is not yet
|
||||
standardized.
|
||||
-->
|
||||
<link rel="help"
|
||||
href="https://github.com/whatwg/html/issues/3223">
|
||||
<link rel="help"
|
||||
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data">
|
||||
<link rel="help"
|
||||
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist">
|
||||
<link rel="help"
|
||||
href="https://w3c.github.io/FileAPI/#file-constructor">
|
||||
<link rel="author" title="Benjamin C. Wiley Sittler"
|
||||
href="mailto:bsittler@chromium.org">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../support/send-file-form-helper.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'ASCII',
|
||||
fileBaseName: 'file-for-upload-in-form.txt',
|
||||
formEncoding: 'windows-1252',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'x-user-defined',
|
||||
fileBaseName: 'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt',
|
||||
formEncoding: 'windows-1252',
|
||||
expectedEncodedBaseName: (
|
||||
'file-for-upload-in-form-.txt'),
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'windows-1252',
|
||||
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
formEncoding: 'windows-1252',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'JIS X 0201 and JIS X 0208',
|
||||
fileBaseName: 'file-for-upload-in-form-★星★.txt',
|
||||
formEncoding: 'windows-1252',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form-★星★.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'Unicode',
|
||||
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
formEncoding: 'windows-1252',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'Unicode',
|
||||
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
|
||||
formEncoding: 'windows-1252',
|
||||
expectedEncodedBaseName: `file-for-upload-in-form-${
|
||||
kTestFallbackWindows1252
|
||||
}.txt`,
|
||||
});
|
||||
|
||||
</script>
|
|
@ -0,0 +1,70 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Upload files in x-user-defined form (tentative)</title>
|
||||
<!--
|
||||
NOTE: This test is tentative because encoding for filename
|
||||
characters unrepresentable in the form charset is not yet
|
||||
standardized.
|
||||
-->
|
||||
<link rel="help"
|
||||
href="https://github.com/whatwg/html/issues/3223">
|
||||
<link rel="help"
|
||||
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data">
|
||||
<link rel="help"
|
||||
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist">
|
||||
<link rel="help"
|
||||
href="https://w3c.github.io/FileAPI/#file-constructor">
|
||||
<link rel="author" title="Benjamin C. Wiley Sittler"
|
||||
href="mailto:bsittler@chromium.org">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../support/send-file-form-helper.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'ASCII',
|
||||
fileBaseName: 'file-for-upload-in-form.txt',
|
||||
formEncoding: 'x-user-defined',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'x-user-defined',
|
||||
fileBaseName: 'file-for-upload-in-form-\uF7F0\uF793\uF783\uF7A0.txt',
|
||||
formEncoding: 'x-user-defined',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form-𓃠.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'windows-1252',
|
||||
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
formEncoding: 'x-user-defined',
|
||||
expectedEncodedBaseName: ('file-for-upload-in-form-' +
|
||||
'☺😂.txt'),
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'JIS X 0201 and JIS X 0208',
|
||||
fileBaseName: 'file-for-upload-in-form-★星★.txt',
|
||||
formEncoding: 'x-user-defined',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form-★星★.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'Unicode',
|
||||
fileBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
formEncoding: 'x-user-defined',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form-☺😂.txt',
|
||||
});
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'Unicode',
|
||||
fileBaseName: `file-for-upload-in-form-${kTestChars}.txt`,
|
||||
formEncoding: 'x-user-defined',
|
||||
expectedEncodedBaseName: `file-for-upload-in-form-${
|
||||
kTestFallbackXUserDefined
|
||||
}.txt`,
|
||||
});
|
||||
|
||||
</script>
|
|
@ -0,0 +1,25 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Upload ASCII-named file in UTF-8 form</title>
|
||||
<link rel="help"
|
||||
href="https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data">
|
||||
<link rel="help"
|
||||
href="https://html.spec.whatwg.org/multipage/dnd.html#datatransferitemlist">
|
||||
<link rel="help"
|
||||
href="https://w3c.github.io/FileAPI/#file-constructor">
|
||||
<link rel="author" title="Benjamin C. Wiley Sittler"
|
||||
href="mailto:bsittler@chromium.org">
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="../support/send-file-form-helper.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
formPostFileUploadTest({
|
||||
fileNameSource: 'ASCII',
|
||||
fileBaseName: 'file-for-upload-in-form.txt',
|
||||
formEncoding: 'UTF-8',
|
||||
expectedEncodedBaseName: 'file-for-upload-in-form.txt',
|
||||
});
|
||||
|
||||
</script>
|
|
@ -30,8 +30,6 @@ setup(function() {
|
|||
request.onload = function() {
|
||||
var idls = request.responseText;
|
||||
|
||||
idl_array.add_untested_idls("[PrimaryGlobal] interface Window { };");
|
||||
|
||||
idl_array.add_untested_idls("[Exposed=(Window,Worker)] interface ArrayBuffer {};");
|
||||
idl_array.add_untested_idls("interface URL {};");
|
||||
idl_array.add_untested_idls("[Exposed=(Window,Worker)] interface EventTarget {};");
|
||||
|
|
|
@ -0,0 +1,249 @@
|
|||
'use strict';
|
||||
|
||||
// Rationale for this particular test character sequence, which is
|
||||
// used in filenames and also in file contents:
|
||||
//
|
||||
// - ABC~ ensures the string starts with something we can read to
|
||||
// ensure it is from the correct source; ~ is used because even
|
||||
// some 1-byte otherwise-ASCII-like parts of ISO-2022-JP
|
||||
// interpret it differently.
|
||||
// - ‾¥ are inside a single-byte range of ISO-2022-JP and help
|
||||
// diagnose problems due to filesystem encoding or locale
|
||||
// - ≈ is inside IBM437 and helps diagnose problems due to filesystem
|
||||
// encoding or locale
|
||||
// - ¤ is inside Latin-1 and helps diagnose problems due to
|
||||
// filesystem encoding or locale; it is also the "simplest" case
|
||||
// needing substitution in ISO-2022-JP
|
||||
// - ・ is inside a single-byte range of ISO-2022-JP in some variants
|
||||
// and helps diagnose problems due to filesystem encoding or locale;
|
||||
// on the web it is distinct when decoding but unified when encoding
|
||||
// - ・ is inside a double-byte range of ISO-2022-JP and helps
|
||||
// diagnose problems due to filesystem encoding or locale
|
||||
// - • is inside Windows-1252 and helps diagnose problems due to
|
||||
// filesystem encoding or locale and also ensures these aren't
|
||||
// accidentally turned into e.g. control codes
|
||||
// - ∙ is inside IBM437 and helps diagnose problems due to filesystem
|
||||
// encoding or locale
|
||||
// - · is inside Latin-1 and helps diagnose problems due to
|
||||
// filesystem encoding or locale and also ensures HTML named
|
||||
// character references (e.g. ·) are not used
|
||||
// - ☼ is inside IBM437 shadowing C0 and helps diagnose problems due to
|
||||
// filesystem encoding or locale and also ensures these aren't
|
||||
// accidentally turned into e.g. control codes
|
||||
// - ★ is inside ISO-2022-JP on a non-Kanji page and makes correct
|
||||
// output easier to spot
|
||||
// - 星 is inside ISO-2022-JP on a Kanji page and makes correct
|
||||
// output easier to spot
|
||||
// - 🌟 is outside the BMP and makes incorrect surrogate pair
|
||||
// substitution detectable and ensures substitutions work
|
||||
// correctly immediately after Kanji 2-byte ISO-2022-JP
|
||||
// - 星 repeated here ensures the correct codec state is used
|
||||
// after a non-BMP substitution
|
||||
// - ★ repeated here also makes correct output easier to spot
|
||||
// - ☼ is inside IBM437 shadowing C0 and helps diagnose problems due to
|
||||
// filesystem encoding or locale and also ensures these aren't
|
||||
// accidentally turned into e.g. control codes and also ensures
|
||||
// substitutions work correctly immediately after non-Kanji
|
||||
// 2-byte ISO-2022-JP
|
||||
// - · is inside Latin-1 and helps diagnose problems due to
|
||||
// filesystem encoding or locale and also ensures HTML named
|
||||
// character references (e.g. ·) are not used
|
||||
// - ∙ is inside IBM437 and helps diagnose problems due to filesystem
|
||||
// encoding or locale
|
||||
// - • is inside Windows-1252 and again helps diagnose problems
|
||||
// due to filesystem encoding or locale
|
||||
// - ・ is inside a double-byte range of ISO-2022-JP and helps
|
||||
// diagnose problems due to filesystem encoding or locale
|
||||
// - ・ is inside a single-byte range of ISO-2022-JP in some variants
|
||||
// and helps diagnose problems due to filesystem encoding or locale;
|
||||
// on the web it is distinct when decoding but unified when encoding
|
||||
// - ¤ is inside Latin-1 and helps diagnose problems due to
|
||||
// filesystem encoding or locale; again it is a "simple"
|
||||
// substitution case
|
||||
// - ≈ is inside IBM437 and helps diagnose problems due to filesystem
|
||||
// encoding or locale
|
||||
// - ¥‾ are inside a single-byte range of ISO-2022-JP and help
|
||||
// diagnose problems due to filesystem encoding or locale
|
||||
// - ~XYZ ensures earlier errors don't lead to misencoding of
|
||||
// simple ASCII
|
||||
//
|
||||
// Overall the near-symmetry makes common I18N mistakes like
|
||||
// off-by-1-after-non-BMP easier to spot. All the characters
|
||||
// are also allowed in Windows Unicode filenames.
|
||||
const kTestChars = 'ABC~‾¥≈¤・・•∙·☼★星🌟星★☼·∙•・・¤≈¥‾~XYZ';
|
||||
|
||||
// NOTE: The expected interpretation of ISO-2022-JP according to
|
||||
// https://encoding.spec.whatwg.org/#iso-2022-jp-encoder unifies
|
||||
// single-byte and double-byte katakana.
|
||||
const kTestFallbackIso2022jp =
|
||||
('ABC~\x1B(J~\\≈¤\x1B$B!&!&\x1B(B•∙·☼\x1B$B!z@1\x1B(B🌟' +
|
||||
'\x1B$B@1!z\x1B(B☼·∙•\x1B$B!&!&\x1B(B¤≈\x1B(J\\~\x1B(B~XYZ').replace(
|
||||
/[^\0-\x7F]/gu,
|
||||
x => `&#${x.codePointAt(0)};`);
|
||||
|
||||
// NOTE: \uFFFD is used here to replace Windows-1252 bytes to match
|
||||
// how we will see them in the reflected POST bytes in a frame using
|
||||
// UTF-8 byte interpretation. The bytes will actually be intact, but
|
||||
// this code cannot tell and does not really care.
|
||||
const kTestFallbackWindows1252 =
|
||||
'ABC~‾\xA5≈\xA4・・\x95∙\xB7☼★星🌟星★☼\xB7∙\x95・・\xA4≈\xA5‾~XYZ'.replace(
|
||||
/[^\0-\xFF]/gu,
|
||||
x => `&#${x.codePointAt(0)};`).replace(/[\x80-\xFF]/g, '\uFFFD');
|
||||
|
||||
const kTestFallbackXUserDefined =
|
||||
kTestChars.replace(/[^\0-\x7F]/gu, x => `&#${x.codePointAt(0)};`);
|
||||
|
||||
// formPostFileUploadTest - verifies multipart upload structure and
|
||||
// numeric character reference replacement for filenames, field names,
|
||||
// and field values.
|
||||
//
|
||||
// Uses /fetch/api/resources/echo-content.py to echo the upload
|
||||
// POST with UTF-8 byte interpretation, leading to the "UTF-8 goggles"
|
||||
// behavior documented below for expectedEncodedBaseName when non-
|
||||
// UTF-8-compatible byte sequences appear in the formEncoding-encoded
|
||||
// uploaded data.
|
||||
//
|
||||
// Fields in the parameter object:
|
||||
//
|
||||
// - fileNameSource: purely explanatory and gives a clue about which
|
||||
// character encoding is the source for the non-7-bit-ASCII parts of
|
||||
// the fileBaseName, or Unicode if no smaller-than-Unicode source
|
||||
// contains all the characters. Used in the test name.
|
||||
// - fileBaseName: the not-necessarily-just-7-bit-ASCII file basename
|
||||
// used for the constructed test file. Used in the test name.
|
||||
// - formEncoding: the acceptCharset of the form used to submit the
|
||||
// test file. Used in the test name.
|
||||
// - expectedEncodedBaseName: the expected formEncoding-encoded
|
||||
// version of fileBaseName with unencodable characters replaced by
|
||||
// numeric character references and non-7-bit-ASCII bytes seen
|
||||
// through UTF-8 goggles; subsequences not interpretable as UTF-8
|
||||
// have each byte represented here by \uFFFD REPLACEMENT CHARACTER.
|
||||
const formPostFileUploadTest = ({
|
||||
fileNameSource,
|
||||
fileBaseName,
|
||||
formEncoding,
|
||||
expectedEncodedBaseName,
|
||||
}) => {
|
||||
promise_test(async testCase => {
|
||||
|
||||
if (document.readyState !== 'complete') {
|
||||
await new Promise(resolve => addEventListener('load', resolve));
|
||||
}
|
||||
|
||||
const formTargetFrame = Object.assign(document.createElement('iframe'), {
|
||||
name: 'formtargetframe',
|
||||
});
|
||||
document.body.append(formTargetFrame);
|
||||
testCase.add_cleanup(() => {
|
||||
document.body.removeChild(formTargetFrame);
|
||||
});
|
||||
|
||||
const form = Object.assign(document.createElement('form'), {
|
||||
acceptCharset: formEncoding,
|
||||
action: '/fetch/api/resources/echo-content.py',
|
||||
method: 'POST',
|
||||
enctype: 'multipart/form-data',
|
||||
target: formTargetFrame.name,
|
||||
});
|
||||
document.body.append(form);
|
||||
testCase.add_cleanup(() => {
|
||||
document.body.removeChild(form);
|
||||
});
|
||||
|
||||
// Used to verify that the browser agrees with the test about
|
||||
// which form charset is used.
|
||||
form.append(Object.assign(document.createElement('input'), {
|
||||
type: 'hidden',
|
||||
name: '_charset_',
|
||||
}));
|
||||
|
||||
// Used to verify that the browser agrees with the test about
|
||||
// field value replacement and encoding independently of file system
|
||||
// idiosyncracies.
|
||||
form.append(Object.assign(document.createElement('input'), {
|
||||
type: 'hidden',
|
||||
name: 'filename',
|
||||
value: fileBaseName,
|
||||
}));
|
||||
|
||||
// Same, but with name and value reversed to ensure field names
|
||||
// get the same treatment.
|
||||
form.append(Object.assign(document.createElement('input'), {
|
||||
type: 'hidden',
|
||||
name: fileBaseName,
|
||||
value: 'filename',
|
||||
}));
|
||||
|
||||
const fileInput = Object.assign(document.createElement('input'), {
|
||||
type: 'file',
|
||||
name: 'file',
|
||||
});
|
||||
form.append(fileInput);
|
||||
|
||||
// Removes c:\fakepath\ or other pseudofolder and returns just the
|
||||
// final component of filePath; allows both / and \ as segment
|
||||
// delimiters.
|
||||
const baseNameOfFilePath = filePath => filePath.split(/[\/\\]/).pop();
|
||||
await new Promise(resolve => {
|
||||
const dataTransfer = new DataTransfer;
|
||||
dataTransfer.items.add(
|
||||
new File([kTestChars], fileBaseName, {type: 'text/plain'}));
|
||||
fileInput.files = dataTransfer.files;
|
||||
// For historical reasons .value will be prefixed with
|
||||
// c:\fakepath\, but the basename should match the file name
|
||||
// exposed through the newer .files[0].name API. This check
|
||||
// verifies that assumption.
|
||||
assert_equals(
|
||||
fileInput.files[0].name,
|
||||
baseNameOfFilePath(fileInput.value),
|
||||
`The basename of the field's value should match its files[0].name`);
|
||||
form.submit();
|
||||
formTargetFrame.onload = resolve;
|
||||
});
|
||||
|
||||
const formDataText = formTargetFrame.contentDocument.body.textContent;
|
||||
const formDataLines = formDataText.split('\n');
|
||||
if (formDataLines.length && !formDataLines[formDataLines.length - 1]) {
|
||||
--formDataLines.length;
|
||||
}
|
||||
assert_greater_than(
|
||||
formDataLines.length,
|
||||
2,
|
||||
`${fileBaseName}: multipart form data must have at least 3 lines: ${
|
||||
JSON.stringify(formDataText)
|
||||
}`);
|
||||
const boundary = formDataLines[0];
|
||||
assert_equals(
|
||||
formDataLines[formDataLines.length - 1],
|
||||
boundary + '--',
|
||||
`${fileBaseName}: multipart form data must end with ${boundary}--: ${
|
||||
JSON.stringify(formDataText)
|
||||
}`);
|
||||
const expectedText = [
|
||||
boundary,
|
||||
'Content-Disposition: form-data; name="_charset_"',
|
||||
'',
|
||||
formEncoding,
|
||||
boundary,
|
||||
'Content-Disposition: form-data; name="filename"',
|
||||
'',
|
||||
expectedEncodedBaseName,
|
||||
boundary,
|
||||
`Content-Disposition: form-data; name="${expectedEncodedBaseName}"`,
|
||||
'',
|
||||
'filename',
|
||||
boundary,
|
||||
`Content-Disposition: form-data; name="file"; ` +
|
||||
`filename="${expectedEncodedBaseName}"`,
|
||||
'Content-Type: text/plain',
|
||||
'',
|
||||
kTestChars,
|
||||
boundary + '--',
|
||||
].join('\n');
|
||||
assert_true(
|
||||
formDataText.startsWith(expectedText),
|
||||
`Unexpected multipart-shaped form data received:\n${
|
||||
formDataText
|
||||
}\nExpected:\n${expectedText}`);
|
||||
}, `Upload ${fileBaseName} (${fileNameSource}) in ${formEncoding} form`);
|
||||
};
|
|
@ -0,0 +1,46 @@
|
|||
<!DOCTYPE html>
|
||||
<meta charset="utf-8">
|
||||
<title>Blob/Unicode interaction: normalization and encoding</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
const OMICRON_WITH_OXIA = '\u1F79'; // NFC normalized to U+3CC
|
||||
const CONTAINS_UNPAIRED_SURROGATES = 'abc\uDC00def\uD800ghi';
|
||||
const REPLACED = 'abc\uFFFDdef\uFFFDghi';
|
||||
|
||||
function readBlobAsPromise(blob) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(blob);
|
||||
reader.onload = () => resolve(reader.result);
|
||||
reader.onerror = () => reject(reader.error);
|
||||
});
|
||||
}
|
||||
|
||||
promise_test(async t => {
|
||||
const blob = new Blob([OMICRON_WITH_OXIA]);
|
||||
const result = await readBlobAsPromise(blob);
|
||||
assert_equals(result, OMICRON_WITH_OXIA, 'String should not be normalized');
|
||||
}, 'Test that strings are not NFC normalized by Blob constructor');
|
||||
|
||||
promise_test(async t => {
|
||||
const file = new File([OMICRON_WITH_OXIA], 'name');
|
||||
const result = await readBlobAsPromise(file);
|
||||
assert_equals(result, OMICRON_WITH_OXIA, 'String should not be normalized');
|
||||
}, 'Test that strings are not NFC normalized by File constructor');
|
||||
|
||||
promise_test(async t => {
|
||||
const blob = new Blob([CONTAINS_UNPAIRED_SURROGATES]);
|
||||
const result = await readBlobAsPromise(blob);
|
||||
assert_equals(result, REPLACED, 'Unpaired surrogates should be replaced.');
|
||||
}, 'Test that unpaired surrogates are replaced by Blob constructor');
|
||||
|
||||
promise_test(async t => {
|
||||
const file = new File([CONTAINS_UNPAIRED_SURROGATES], 'name');
|
||||
const result = await readBlobAsPromise(file);
|
||||
assert_equals(result, REPLACED, 'Unpaired surrogates should be replaced.');
|
||||
}, 'Test that unpaired surrogates are replaced by File constructor');
|
||||
|
||||
</script>
|
|
@ -20,9 +20,8 @@
|
|||
http.onloadend = function() {
|
||||
var fileDisplay = document.querySelector("#fileDisplay");
|
||||
fileDisplay.src = window.URL.createObjectURL(http.response);
|
||||
takeScreenshot();
|
||||
fileDisplay.onload = takeScreenshot;
|
||||
};
|
||||
http.send();
|
||||
</script>
|
||||
</html>
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ async_test(function(t) {
|
|||
var idls = request.responseText;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/browsers.html#window
|
||||
idlArray.add_untested_idls("[PrimaryGlobal] interface Window { };");
|
||||
idlArray.add_untested_idls("[Global=Window, Exposed=Window] interface Window { };");
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/webappapis.html#windoworworkerglobalscope-mixin
|
||||
idlArray.add_untested_idls(`[NoInterfaceObject, Exposed=(Window,Worker)]
|
||||
|
|
|
@ -53,8 +53,15 @@ Running Tests Manually
|
|||
======================
|
||||
|
||||
The test server can be started using
|
||||
```
|
||||
./wpt serve
|
||||
```
|
||||
|
||||
./wpt serve
|
||||
**On Windows**: You will need to preceed the prior command with
|
||||
`python` or the path to the python binary.
|
||||
```bash
|
||||
python wpt serve
|
||||
```
|
||||
|
||||
This will start HTTP servers on two ports and a websockets server on
|
||||
one port. By default one web server starts on port 8000 and the other
|
||||
|
@ -82,12 +89,15 @@ file setup documented above, but you must *not* have the
|
|||
test server already running when calling `wpt run`. The basic command
|
||||
line syntax is:
|
||||
|
||||
```
|
||||
```bash
|
||||
./wpt run product [tests]
|
||||
```
|
||||
|
||||
**On Windows**: You will need to preceed the prior command with
|
||||
`python` or the path to the python binary.
|
||||
```bash
|
||||
python wpt product [tests]
|
||||
```
|
||||
|
||||
where `product` is currently `firefox` or `chrome` and `[tests]` is a
|
||||
list of paths to tests. This will attempt to automatically locate a
|
||||
|
@ -114,7 +124,7 @@ brew install nss
|
|||
|
||||
On other platforms, download the firefox archive and common.tests.zip
|
||||
archive for your platform from
|
||||
[https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/](Mozilla CI)
|
||||
[Mozilla CI](https://archive.mozilla.org/pub/firefox/nightly/latest-mozilla-central/).
|
||||
|
||||
Then extract `certutil[.exe]` from the tests.zip package and
|
||||
`libnss3[.so|.dll|.dynlib]` and put the former on your path and the latter on
|
||||
|
@ -187,9 +197,13 @@ then remove the `tools` and `resources` directories, as above.
|
|||
<span id="windows-notes">Windows Notes</span>
|
||||
=============================================
|
||||
|
||||
On Windows `wpt` commands mut bre prefixed with `python` or the path
|
||||
On Windows `wpt` commands must be prefixed with `python` or the path
|
||||
to the python binary (if `python` is not in your `%PATH%`).
|
||||
|
||||
```bash
|
||||
python wpt [command]
|
||||
```
|
||||
|
||||
Alternatively, you may also use
|
||||
[Bash on Ubuntu on Windows](https://msdn.microsoft.com/en-us/commandline/wsl/about)
|
||||
in the Windows 10 Anniversary Update build, then access your windows
|
||||
|
|
|
@ -29,11 +29,6 @@ setup(function() {
|
|||
request.onload = function() {
|
||||
var idls = request.responseText;
|
||||
|
||||
idl_array.add_untested_idls("[PrimaryGlobal] interface Window { };");
|
||||
|
||||
idl_array.add_untested_idls("interface ArrayBuffer {};");
|
||||
idl_array.add_untested_idls("interface ArrayBufferView {};");
|
||||
|
||||
idl_array.add_idls(idls);
|
||||
|
||||
idl_array.add_objects({"Crypto":["crypto"], "SubtleCrypto":["crypto.subtle"]});
|
||||
|
|
|
@ -29,11 +29,6 @@ setup(function() {
|
|||
request.onload = function() {
|
||||
var idls = request.responseText;
|
||||
|
||||
idl_array.add_untested_idls("[PrimaryGlobal] interface Window { };");
|
||||
|
||||
idl_array.add_untested_idls("interface ArrayBuffer {};");
|
||||
idl_array.add_untested_idls("interface ArrayBufferView {};");
|
||||
|
||||
idl_array.add_idls(idls);
|
||||
|
||||
idl_array.add_objects({"Crypto":["crypto"], "SubtleCrypto":["crypto.subtle"]});
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open("POST", "http://nonexistent-origin.{{host}}}:{{ports[http][0]}}", false);
|
||||
xhr.open("POST", "http://nonexistent-origin.{{host}}:{{ports[http][0]}}", false);
|
||||
|
||||
assert_throws("NetworkError", function()
|
||||
{
|
||||
|
@ -25,6 +25,12 @@
|
|||
});
|
||||
assert_equals(xhr.readyState, 4)
|
||||
|
||||
}, "http URL");
|
||||
|
||||
test(function()
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
xhr.open("GET", "data:text/html;charset=utf-8;base64,PT0NUWVBFIGh0bWw%2BDQo8", false);
|
||||
|
||||
assert_throws("NetworkError", function()
|
||||
|
@ -33,7 +39,7 @@
|
|||
});
|
||||
assert_equals(xhr.readyState, 4)
|
||||
|
||||
});
|
||||
}, "data URL");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -15,8 +15,8 @@
|
|||
<div id="log"></div>
|
||||
|
||||
<script type="text/javascript">
|
||||
function testRedirectPost(code, shouldResendPost) {
|
||||
var test = async_test(document.title + " (" + code + ")");
|
||||
function testRedirectPost(params) {
|
||||
var test = async_test(document.title + " (" + params.name + ")");
|
||||
var actual = [];
|
||||
// We check upload.onprogress with a boolean because it *might* fire more than once
|
||||
var progressFiredReadyState1 = false;
|
||||
|
@ -24,13 +24,13 @@
|
|||
var expectedHeaders, expectedEvents;
|
||||
|
||||
// 307 redirects should resend the POST data, and events and headers will be a little different..
|
||||
if(shouldResendPost) {
|
||||
if(params.expectResendPost) {
|
||||
expectedHeaders = {
|
||||
"X-Request-Content-Length": "11988",
|
||||
"X-Request-Content-Length": "12000",
|
||||
"X-Request-Content-Type": "text/plain;charset=UTF-8",
|
||||
"X-Request-Method": "POST",
|
||||
"X-Request-Query": "NO",
|
||||
"Content-Length": "11988"
|
||||
"Content-Length": "12000"
|
||||
}
|
||||
expectedEvents = [
|
||||
"xhr onreadystatechange 1",
|
||||
|
@ -63,6 +63,10 @@
|
|||
"xhr loadend 4"
|
||||
];
|
||||
}
|
||||
// Override expectations if provided.
|
||||
if(params.expectedContentType)
|
||||
expectedHeaders["X-Request-Content-Type"] = params.expectedContentType;
|
||||
|
||||
test.step(function()
|
||||
{
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
@ -105,20 +109,24 @@
|
|||
}
|
||||
|
||||
assert_array_equals(actual, expectedEvents, "events firing in expected order and states");
|
||||
if (params.expectedBody)
|
||||
assert_equals(xhr.response, params.expectedBody, 'request body was resent');
|
||||
test.done();
|
||||
});
|
||||
|
||||
xhr.open("POST", "./resources/redirect.py?location=content.py&code=" + code, true);
|
||||
xhr.send((new Array(1000)).join("Test Message"));
|
||||
xhr.open("POST", "./resources/redirect.py?location=content.py&code=" + params.code, true);
|
||||
xhr.send(params.body);
|
||||
});
|
||||
}
|
||||
|
||||
const stringBody = "Test Message".repeat(1000);
|
||||
const blobBody = new Blob(new Array(1000).fill("Test Message"));
|
||||
|
||||
testRedirectPost(301, false);
|
||||
testRedirectPost(302, false);
|
||||
testRedirectPost(303, false);
|
||||
testRedirectPost(307, true);
|
||||
|
||||
testRedirectPost({name: "301", code: 301, expectResendPost: false, body: stringBody});
|
||||
testRedirectPost({name: "302", code: 302, expectResendPost: false, body: stringBody});
|
||||
testRedirectPost({name: "303", code: 303, expectResendPost: false, body: stringBody});
|
||||
testRedirectPost({name: "307 (string)", code: 307, expectResendPost: true, body: stringBody, expectedBody: stringBody });
|
||||
testRedirectPost({name: "307 (blob)", code: 307, expectResendPost: true, body: blobBody, expectedBody: stringBody, expectedContentType: "NO" });
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/feature-policy/resources/featurepolicy.js"></script>
|
||||
<script src="/generic-sensor/generic-sensor-feature-policy-test.sub.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
run_fp_tests_disabled(Accelerometer);
|
||||
run_fp_tests_disabled(LinearAccelerationSensor);
|
||||
run_fp_tests_disabled(GravitySensor);
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1 @@
|
|||
Feature-Policy: accelerometer 'none'
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/feature-policy/resources/featurepolicy.js"></script>
|
||||
<script src="/generic-sensor/generic-sensor-feature-policy-test.sub.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
run_fp_tests_enabled_by_attribute_redirect_on_load(Accelerometer);
|
||||
run_fp_tests_enabled_by_attribute_redirect_on_load(LinearAccelerationSensor);
|
||||
run_fp_tests_enabled_by_attribute_redirect_on_load(GravitySensor);
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/feature-policy/resources/featurepolicy.js"></script>
|
||||
<script src="/generic-sensor/generic-sensor-feature-policy-test.sub.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
run_fp_tests_enabled_by_attribute(Accelerometer);
|
||||
run_fp_tests_enabled_by_attribute(LinearAccelerationSensor);
|
||||
run_fp_tests_enabled_by_attribute(GravitySensor);
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/feature-policy/resources/featurepolicy.js"></script>
|
||||
<script src="/generic-sensor/generic-sensor-feature-policy-test.sub.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
run_fp_tests_enabled(Accelerometer);
|
||||
run_fp_tests_enabled(LinearAccelerationSensor);
|
||||
run_fp_tests_enabled(GravitySensor);
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1 @@
|
|||
Feature-Policy: accelerometer *
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/feature-policy/resources/featurepolicy.js"></script>
|
||||
<script src="/generic-sensor/generic-sensor-feature-policy-test.sub.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
run_fp_tests_enabled_on_self_origin(Accelerometer);
|
||||
run_fp_tests_enabled_on_self_origin(LinearAccelerationSensor);
|
||||
run_fp_tests_enabled_on_self_origin(GravitySensor);
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1 @@
|
|||
Feature-Policy: accelerometer 'self'
|
|
@ -10,5 +10,6 @@
|
|||
<script>
|
||||
|
||||
runGenericSensorTests(Accelerometer);
|
||||
runGenericSensorTests(GravitySensor);
|
||||
|
||||
</script>
|
||||
|
|
|
@ -16,5 +16,6 @@
|
|||
<script>
|
||||
|
||||
runGenericSensorInsecureContext("Accelerometer");
|
||||
runGenericSensorInsecureContext("GravitySensor");
|
||||
|
||||
</script>
|
||||
|
|
|
@ -16,5 +16,6 @@
|
|||
<script>
|
||||
|
||||
runGenericSensorOnerror(Accelerometer);
|
||||
runGenericSensorOnerror(GravitySensor);
|
||||
|
||||
</script>
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/feature-policy/resources/featurepolicy.js"></script>
|
||||
<script src="/generic-sensor/generic-sensor-feature-policy-test.sub.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
run_fp_tests_disabled(AmbientLightSensor);
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1 @@
|
|||
Feature-Policy: ambient-light-sensor 'none'
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/feature-policy/resources/featurepolicy.js"></script>
|
||||
<script src="/generic-sensor/generic-sensor-feature-policy-test.sub.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
run_fp_tests_enabled_by_attribute_redirect_on_load(AmbientLightSensor);
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/feature-policy/resources/featurepolicy.js"></script>
|
||||
<script src="/generic-sensor/generic-sensor-feature-policy-test.sub.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
run_fp_tests_enabled_by_attribute(AmbientLightSensor);
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/feature-policy/resources/featurepolicy.js"></script>
|
||||
<script src="/generic-sensor/generic-sensor-feature-policy-test.sub.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
run_fp_tests_enabled(AmbientLightSensor);
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1 @@
|
|||
Feature-Policy: ambient-light-sensor *
|
|
@ -0,0 +1,12 @@
|
|||
<!DOCTYPE html>
|
||||
<body>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/feature-policy/resources/featurepolicy.js"></script>
|
||||
<script src="/generic-sensor/generic-sensor-feature-policy-test.sub.js"></script>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
run_fp_tests_enabled_on_self_origin(AmbientLightSensor);
|
||||
</script>
|
||||
</body>
|
|
@ -0,0 +1 @@
|
|||
Feature-Policy: ambient-light-sensor 'self'
|
|
@ -0,0 +1,2 @@
|
|||
@toddreifsteck
|
||||
@igrigorik
|
|
@ -0,0 +1,12 @@
|
|||
# Web Bluetooth Testing
|
||||
|
||||
Web Bluetooth testing relies on the [Web Bluetooth Testing API] which must be
|
||||
provided by browsers under test.
|
||||
|
||||
In this test suite `resources/bluetooth-helpers.js` detects and triggers
|
||||
the API to be loaded as needed.
|
||||
|
||||
The Chromium implementation is provided by
|
||||
`../resources/chromium/web-bluetooth-test.js`.
|
||||
|
||||
[Web Bluetooth Testing API]: https://docs.google.com/document/d/1Nhv_oVDCodd1pEH_jj9k8gF4rPGb_84VYaZ9IG8M_WY/
|
|
@ -0,0 +1,166 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
'use strict'
|
||||
|
||||
var base_uuid = '00000000-0000-1000-8000-00805f9b34fb'
|
||||
|
||||
test(() => {
|
||||
let base_alias = 0x0
|
||||
assert_equals(BluetoothUUID.getService(base_alias), base_uuid);
|
||||
assert_equals(BluetoothUUID.getCharacteristic(base_alias), base_uuid);
|
||||
assert_equals(BluetoothUUID.getDescriptor(base_alias), base_uuid);
|
||||
}, '0x0 should produce valid UUID.');
|
||||
|
||||
test(() => {
|
||||
assert_equals(BluetoothUUID.getService(NaN), base_uuid);
|
||||
assert_equals(BluetoothUUID.getCharacteristic(NaN), base_uuid);
|
||||
assert_equals(BluetoothUUID.getDescriptor(NaN), base_uuid);
|
||||
}, 'NaN returns basic uuid');
|
||||
|
||||
test(() => {
|
||||
let max_uuid = 'ffffffff-0000-1000-8000-00805f9b34fb';
|
||||
let nine_digits = 0xfffffffff;
|
||||
let thirteen_digits = 0xfffffffffffff;
|
||||
let fourteen_digits = 0xffffffffffffff;
|
||||
assert_equals(BluetoothUUID.getService(nine_digits), max_uuid);
|
||||
assert_equals(BluetoothUUID.getCharacteristic(nine_digits), max_uuid);
|
||||
assert_equals(BluetoothUUID.getDescriptor(nine_digits), max_uuid);
|
||||
assert_equals(BluetoothUUID.getService(thirteen_digits), max_uuid);
|
||||
assert_equals(BluetoothUUID.getCharacteristic(thirteen_digits), max_uuid);
|
||||
assert_equals(BluetoothUUID.getDescriptor(thirteen_digits), max_uuid);
|
||||
assert_equals(BluetoothUUID.getService(fourteen_digits), base_uuid);
|
||||
assert_equals(BluetoothUUID.getCharacteristic(fourteen_digits), base_uuid);
|
||||
assert_equals(BluetoothUUID.getDescriptor(fourteen_digits), base_uuid);
|
||||
}, 'Values between 0xfffffffff (8 digits) and 0xffffffffffffff (14 digits)' +
|
||||
'should return max UUID');
|
||||
|
||||
test(() => {
|
||||
assert_equals(BluetoothUUID.getService(Infinity), base_uuid);
|
||||
assert_equals(BluetoothUUID.getCharacteristic(Infinity), base_uuid);
|
||||
assert_equals(BluetoothUUID.getDescriptor(Infinity), base_uuid);
|
||||
}, 'Infinity returns base UUID');
|
||||
|
||||
test(() => {
|
||||
let deadbeef_alias = 0xDEADBEEF;
|
||||
let deadbeef_uuid = 'deadbeef-0000-1000-8000-00805f9b34fb';
|
||||
assert_equals(BluetoothUUID.getService(deadbeef_alias), deadbeef_uuid);
|
||||
assert_equals(BluetoothUUID.getCharacteristic(deadbeef_alias), deadbeef_uuid);
|
||||
assert_equals(BluetoothUUID.getDescriptor(deadbeef_alias), deadbeef_uuid);
|
||||
}, '0xdeadbeef should produce valid UUID.');
|
||||
|
||||
test(() => {
|
||||
let adeadbeef_alias = 0xADEADBEEF;
|
||||
let adeadbeef_uuid = 'deadbeef-0000-1000-8000-00805f9b34fb';
|
||||
assert_equals(BluetoothUUID.getService(adeadbeef_alias), adeadbeef_uuid);
|
||||
assert_equals(BluetoothUUID.getCharacteristic(adeadbeef_alias), adeadbeef_uuid);
|
||||
assert_equals(BluetoothUUID.getDescriptor(adeadbeef_alias), adeadbeef_uuid);
|
||||
}, 'Only first 32bits should be used.');
|
||||
|
||||
test(() => {
|
||||
let basic_uuid = '1a2b3c4d-5e6f-7a8b-9c0d-1e2f3a4b5c6d';
|
||||
assert_equals(BluetoothUUID.getService(basic_uuid), basic_uuid);
|
||||
assert_equals(BluetoothUUID.getCharacteristic(basic_uuid), basic_uuid);
|
||||
assert_equals(BluetoothUUID.getDescriptor(basic_uuid), basic_uuid);
|
||||
}, 'A valid UUID String should return the same UUID.');
|
||||
|
||||
test(() => {
|
||||
let all_caps_uuid = '1A2B3C4D-5E6F-7A8B-9C0D-1E2F3A4B5C6D';
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getService(all_caps_uuid));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(all_caps_uuid));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(all_caps_uuid));
|
||||
}, 'A UUID String with uppercase letters is an invalid UUID.');
|
||||
|
||||
test(() => {
|
||||
let string_alias = 'deadbeef';
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getService(string_alias));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(string_alias));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(string_alias));
|
||||
}, 'A 32bit *String* alias is invalid.');
|
||||
|
||||
test(() => {
|
||||
let invalid_character_uuid = '0000000g-0000-1000-8000-00805f9b34fb';
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getService(invalid_character_uuid));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(invalid_character_uuid));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(invalid_character_uuid));
|
||||
}, 'A UUID with invalid characters is an invalid UUID.');
|
||||
|
||||
test(() => {
|
||||
assert_equals(BluetoothUUID.getService('alert_notification'),
|
||||
'00001811-0000-1000-8000-00805f9b34fb');
|
||||
assert_equals(BluetoothUUID.getCharacteristic('aerobic_heart_rate_lower_limit'),
|
||||
'00002a7e-0000-1000-8000-00805f9b34fb');
|
||||
assert_equals(BluetoothUUID.getDescriptor('gatt.characteristic_extended_properties'),
|
||||
'00002900-0000-1000-8000-00805f9b34fb');
|
||||
}, 'A valid UUID from a name.');
|
||||
|
||||
test(() => {
|
||||
assert_throws(TypeError(), () => {
|
||||
BluetoothUUID.getService('aerobic_heart_rate_lower_limit');
|
||||
});
|
||||
assert_throws(TypeError(), () => {
|
||||
BluetoothUUID.getService('gatt.characteristic_extended_properties');
|
||||
});
|
||||
assert_throws(TypeError(), () => {
|
||||
BluetoothUUID.getCharacteristic('alert_notification');
|
||||
});
|
||||
assert_throws(TypeError(), () => {
|
||||
BluetoothUUID.getCharacteristic('gatt.characteristic_extended_properties');
|
||||
});
|
||||
assert_throws(TypeError(), () => {
|
||||
BluetoothUUID.getDescriptor('alert_notification');
|
||||
});
|
||||
assert_throws(TypeError(), () => {
|
||||
BluetoothUUID.getDescriptor('aerobic_heart_rate_lower_limit');
|
||||
});
|
||||
}, 'Make sure attributes don\'t share a map');
|
||||
|
||||
test(() => {
|
||||
let wrong_name = 'wrong_name';
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getService(wrong_name));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(wrong_name));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(wrong_name));
|
||||
}, 'Invalid Descriptor name');
|
||||
|
||||
test(() => {
|
||||
let object = {};
|
||||
let array = [];
|
||||
let func = () => {};
|
||||
|
||||
// cannonicalUUID
|
||||
assert_throws(new TypeError, () => BluetoothUUID.canonicalUUID(object));
|
||||
// [] converts to '', which converts to 0 before the range check.
|
||||
assert_equals(BluetoothUUID.canonicalUUID(array), base_uuid);
|
||||
assert_throws(new TypeError, () => BluetoothUUID.canonicalUUID(func));
|
||||
assert_throws(new TypeError, () => BluetoothUUID.canonicalUUID(undefined));
|
||||
assert_equals(BluetoothUUID.canonicalUUID(null), base_uuid);
|
||||
assert_equals(BluetoothUUID.canonicalUUID(false), base_uuid);
|
||||
assert_equals(BluetoothUUID.canonicalUUID(true), BluetoothUUID.canonicalUUID(1));
|
||||
assert_throws(new TypeError, () => BluetoothUUID.canonicalUUID(NaN));
|
||||
|
||||
// getService
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getService(object));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getService(array));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getService(func));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getService(undefined));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getService(null));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getService(false));
|
||||
|
||||
// getCharacteristic
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(object));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(array));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(func));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(undefined));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(null));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getCharacteristic(false));
|
||||
|
||||
// getDescriptor
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(object));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(array));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(func));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(undefined));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(null));
|
||||
assert_throws(TypeError(), () => BluetoothUUID.getDescriptor(false));
|
||||
}, 'Non-number and non-strings');
|
||||
</script>
|
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
test(() => {
|
||||
assert_true('bluetooth' in navigator,
|
||||
'navigator.bluetooth exists.');
|
||||
}, 'navigator.bluetooth IDL test');
|
||||
|
||||
test(() => {
|
||||
assert_equals(navigator.bluetooth, navigator.bluetooth);
|
||||
}, '[SameObject] test for navigator.bluetooth');
|
||||
</script>
|
|
@ -0,0 +1,800 @@
|
|||
'use strict';
|
||||
|
||||
function loadScript(path) {
|
||||
let script = document.createElement('script');
|
||||
let promise = new Promise(resolve => script.onload = resolve);
|
||||
script.src = path;
|
||||
script.async = false;
|
||||
document.head.appendChild(script);
|
||||
return promise;
|
||||
}
|
||||
|
||||
function loadScripts(paths) {
|
||||
let chain = Promise.resolve();
|
||||
for (let path of paths) {
|
||||
chain = chain.then(() => loadScript(path));
|
||||
}
|
||||
return chain;
|
||||
}
|
||||
|
||||
function performChromiumSetup() {
|
||||
// Make sure we are actually on Chromium.
|
||||
if (!Mojo) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the Chromium-specific resources.
|
||||
let prefix = '/resources/chromium';
|
||||
let extra = [];
|
||||
if (window.location.pathname.includes('/LayoutTests/')) {
|
||||
let root = window.location.pathname.match(/.*LayoutTests/);
|
||||
prefix = `${root}/external/wpt/resources/chromium`;
|
||||
extra = [
|
||||
`${root}/resources/bluetooth/bluetooth-fake-adapter.js`,
|
||||
];
|
||||
} else if (window.location.pathname.startsWith('/bluetooth/https/')) {
|
||||
extra = [
|
||||
'/js-test-resources/bluetooth/bluetooth-fake-adapter.js',
|
||||
];
|
||||
}
|
||||
return loadScripts([
|
||||
`${prefix}/mojo_bindings.js`,
|
||||
`${prefix}/mojo_layouttest_test.mojom.js`,
|
||||
`${prefix}/uuid.mojom.js`,
|
||||
`${prefix}/fake_bluetooth.mojom.js`,
|
||||
`${prefix}/web-bluetooth-test.js`,
|
||||
].concat(extra))
|
||||
// Call setBluetoothFakeAdapter() to clean up any fake adapters left over
|
||||
// by legacy tests.
|
||||
// Legacy tests that use setBluetoothFakeAdapter() sometimes fail to clean
|
||||
// their fake adapter. This is not a problem for these tests because the
|
||||
// next setBluetoothFakeAdapter() will clean it up anyway but it is a
|
||||
// problem for the new tests that do not use setBluetoothFakeAdapter().
|
||||
// TODO(crbug.com/569709): Remove once setBluetoothFakeAdapter is no
|
||||
// longer used.
|
||||
.then(() => setBluetoothFakeAdapter ? setBluetoothFakeAdapter('')
|
||||
: undefined);
|
||||
}
|
||||
|
||||
|
||||
// These tests rely on the User Agent providing an implementation of the
|
||||
// Web Bluetooth Testing API.
|
||||
// https://docs.google.com/document/d/1Nhv_oVDCodd1pEH_jj9k8gF4rPGb_84VYaZ9IG8M_WY/edit?ts=59b6d823#heading=h.7nki9mck5t64
|
||||
function bluetooth_test(func, name, properties) {
|
||||
Promise.resolve()
|
||||
.then(() => promise_test(t => Promise.resolve()
|
||||
// Trigger Chromium-specific setup.
|
||||
.then(performChromiumSetup)
|
||||
.then(() => func(t)), name, properties));
|
||||
}
|
||||
|
||||
// HCI Error Codes. Used for simulateGATT[Dis]ConnectionResponse.
|
||||
// For a complete list of possible error codes see
|
||||
// BT 4.2 Vol 2 Part D 1.3 List Of Error Codes.
|
||||
const HCI_SUCCESS = 0x0000;
|
||||
const HCI_CONNECTION_TIMEOUT = 0x0008;
|
||||
|
||||
// GATT Error codes. Used for GATT operations responses.
|
||||
// BT 4.2 Vol 3 Part F 3.4.1.1 Error Response
|
||||
const GATT_SUCCESS = 0x0000;
|
||||
const GATT_INVALID_HANDLE = 0x0001;
|
||||
|
||||
// Bluetooth UUID constants:
|
||||
// Services:
|
||||
var blocklist_test_service_uuid = "611c954a-263b-4f4a-aab6-01ddb953f985";
|
||||
var request_disconnection_service_uuid = "01d7d889-7451-419f-aeb8-d65e7b9277af";
|
||||
// Characteristics:
|
||||
var blocklist_exclude_reads_characteristic_uuid =
|
||||
"bad1c9a2-9a5b-4015-8b60-1579bbbf2135";
|
||||
var request_disconnection_characteristic_uuid =
|
||||
"01d7d88a-7451-419f-aeb8-d65e7b9277af";
|
||||
// Descriptors:
|
||||
var blocklist_test_descriptor_uuid = "bad2ddcf-60db-45cd-bef9-fd72b153cf7c";
|
||||
|
||||
// Sometimes we need to test that using either the name, alias, or UUID
|
||||
// produces the same result. The following objects help us do that.
|
||||
var generic_access = {
|
||||
alias: 0x1800,
|
||||
name: 'generic_access',
|
||||
uuid: '00001800-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
var device_name = {
|
||||
alias: 0x2a00,
|
||||
name: 'gap.device_name',
|
||||
uuid: '00002a00-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
var reconnection_address = {
|
||||
alias: 0x2a03,
|
||||
name: 'gap.reconnection_address',
|
||||
uuid: '00002a03-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
var heart_rate = {
|
||||
alias: 0x180d,
|
||||
name: 'heart_rate',
|
||||
uuid: '0000180d-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
var health_thermometer = {
|
||||
alias: 0x1809,
|
||||
name: 'health_thermometer',
|
||||
uuid: '00001809-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
var body_sensor_location = {
|
||||
alias: 0x2a38,
|
||||
name: 'body_sensor_location',
|
||||
uuid: '00002a38-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
var glucose = {
|
||||
alias: 0x1808,
|
||||
name: 'glucose',
|
||||
uuid: '00001808-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
var battery_service = {
|
||||
alias: 0x180f,
|
||||
name: 'battery_service',
|
||||
uuid: '0000180f-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
var battery_level = {
|
||||
alias: 0x2A19,
|
||||
name: 'battery_level',
|
||||
uuid: '00002a19-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
var user_description = {
|
||||
alias: 0x2901,
|
||||
name: 'gatt.characteristic_user_description',
|
||||
uuid: '00002901-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
var client_characteristic_configuration = {
|
||||
alias: 0x2902,
|
||||
name: 'gatt.client_characteristic_configuration',
|
||||
uuid: '00002902-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
var measurement_interval = {
|
||||
alias: 0x2a21,
|
||||
name: 'measurement_interval',
|
||||
uuid: '00002a21-0000-1000-8000-00805f9b34fb'
|
||||
};
|
||||
|
||||
// The following tests make sure the Web Bluetooth implementation
|
||||
// responds correctly to the different types of errors the
|
||||
// underlying platform might return for GATT operations.
|
||||
|
||||
// Each browser should map these characteristics to specific code paths
|
||||
// that result in different errors thus increasing code coverage
|
||||
// when testing. Therefore some of these characteristics might not be useful
|
||||
// for all browsers.
|
||||
//
|
||||
// TODO(ortuno): According to the testing spec errorUUID(0x101) to
|
||||
// errorUUID(0x1ff) should be use for the uuids of the characteristics.
|
||||
var gatt_errors_tests = [{
|
||||
testName: 'GATT Error: Unknown.',
|
||||
uuid: errorUUID(0xA1),
|
||||
error: new DOMException(
|
||||
'GATT Error Unknown.',
|
||||
'NotSupportedError')
|
||||
}, {
|
||||
testName: 'GATT Error: Failed.',
|
||||
uuid: errorUUID(0xA2),
|
||||
error: new DOMException(
|
||||
'GATT operation failed for unknown reason.',
|
||||
'NotSupportedError')
|
||||
}, {
|
||||
testName: 'GATT Error: In Progress.',
|
||||
uuid: errorUUID(0xA3),
|
||||
error: new DOMException(
|
||||
'GATT operation already in progress.',
|
||||
'NetworkError')
|
||||
}, {
|
||||
testName: 'GATT Error: Invalid Length.',
|
||||
uuid: errorUUID(0xA4),
|
||||
error: new DOMException(
|
||||
'GATT Error: invalid attribute length.',
|
||||
'InvalidModificationError')
|
||||
}, {
|
||||
testName: 'GATT Error: Not Permitted.',
|
||||
uuid: errorUUID(0xA5),
|
||||
error: new DOMException(
|
||||
'GATT operation not permitted.',
|
||||
'NotSupportedError')
|
||||
}, {
|
||||
testName: 'GATT Error: Not Authorized.',
|
||||
uuid: errorUUID(0xA6),
|
||||
error: new DOMException(
|
||||
'GATT operation not authorized.',
|
||||
'SecurityError')
|
||||
}, {
|
||||
testName: 'GATT Error: Not Paired.',
|
||||
uuid: errorUUID(0xA7),
|
||||
// TODO(ortuno): Change to InsufficientAuthenticationError or similiar
|
||||
// once https://github.com/WebBluetoothCG/web-bluetooth/issues/137 is
|
||||
// resolved.
|
||||
error: new DOMException(
|
||||
'GATT Error: Not paired.',
|
||||
'NetworkError')
|
||||
}, {
|
||||
testName: 'GATT Error: Not Supported.',
|
||||
uuid: errorUUID(0xA8),
|
||||
error: new DOMException(
|
||||
'GATT Error: Not supported.',
|
||||
'NotSupportedError')
|
||||
}];
|
||||
|
||||
function callWithTrustedClick(callback) {
|
||||
return new Promise(resolve => {
|
||||
let button = document.createElement('button');
|
||||
button.textContent = 'click to continue test';
|
||||
button.style.display = 'block';
|
||||
button.style.fontSize = '20px';
|
||||
button.style.padding = '10px';
|
||||
button.onclick = () => {
|
||||
document.body.removeChild(button);
|
||||
resolve(callback());
|
||||
};
|
||||
document.body.appendChild(button);
|
||||
test_driver.click(button);
|
||||
});
|
||||
}
|
||||
|
||||
// Calls requestDevice() in a context that's 'allowed to show a popup'.
|
||||
function requestDeviceWithTrustedClick() {
|
||||
let args = arguments;
|
||||
return callWithTrustedClick(
|
||||
() => navigator.bluetooth.requestDevice.apply(navigator.bluetooth, args));
|
||||
}
|
||||
|
||||
// errorUUID(alias) returns a UUID with the top 32 bits of
|
||||
// '00000000-97e5-4cd7-b9f1-f5a427670c59' replaced with the bits of |alias|.
|
||||
// For example, errorUUID(0xDEADBEEF) returns
|
||||
// 'deadbeef-97e5-4cd7-b9f1-f5a427670c59'. The bottom 96 bits of error UUIDs
|
||||
// were generated as a type 4 (random) UUID.
|
||||
function errorUUID(uuidAlias) {
|
||||
// Make the number positive.
|
||||
uuidAlias >>>= 0;
|
||||
// Append the alias as a hex number.
|
||||
var strAlias = '0000000' + uuidAlias.toString(16);
|
||||
// Get last 8 digits of strAlias.
|
||||
strAlias = strAlias.substr(-8);
|
||||
// Append Base Error UUID
|
||||
return strAlias + '-97e5-4cd7-b9f1-f5a427670c59';
|
||||
}
|
||||
|
||||
// Function to test that a promise rejects with the expected error type and
|
||||
// message.
|
||||
function assert_promise_rejects_with_message(promise, expected, description) {
|
||||
return promise.then(() => {
|
||||
assert_unreached('Promise should have rejected: ' + description);
|
||||
}, error => {
|
||||
assert_equals(error.name, expected.name, 'Unexpected Error Name:');
|
||||
if (expected.message) {
|
||||
assert_equals(error.message, expected.message, 'Unexpected Error Message:');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function runGarbageCollection()
|
||||
{
|
||||
// Run gc() as a promise.
|
||||
return new Promise(
|
||||
function(resolve, reject) {
|
||||
GCController.collect();
|
||||
step_timeout(resolve, 0);
|
||||
});
|
||||
}
|
||||
|
||||
function eventPromise(target, type, options) {
|
||||
return new Promise(resolve => {
|
||||
let wrapper = function(event) {
|
||||
target.removeEventListener(type, wrapper);
|
||||
resolve(event);
|
||||
};
|
||||
target.addEventListener(type, wrapper, options);
|
||||
});
|
||||
}
|
||||
|
||||
// Helper function to assert that events are fired and a promise resolved
|
||||
// in the correct order.
|
||||
// 'event' should be passed as |should_be_first| to indicate that the events
|
||||
// should be fired first, otherwise 'promiseresolved' should be passed.
|
||||
// Attaches |num_listeners| |event| listeners to |object|. If all events have
|
||||
// been fired and the promise resolved in the correct order, returns a promise
|
||||
// that fulfills with the result of |object|.|func()| and |event.target.value|
|
||||
// of each of event listeners. Otherwise throws an error.
|
||||
function assert_promise_event_order_(should_be_first, object, func, event, num_listeners) {
|
||||
let order = [];
|
||||
let event_promises = [];
|
||||
for (let i = 0; i < num_listeners; i++) {
|
||||
event_promises.push(new Promise(resolve => {
|
||||
let event_listener = (e) => {
|
||||
object.removeEventListener(event, event_listener);
|
||||
order.push('event');
|
||||
resolve(e.target.value);
|
||||
};
|
||||
object.addEventListener(event, event_listener);
|
||||
}));
|
||||
}
|
||||
|
||||
let func_promise = object[func]().then(result => {
|
||||
order.push('promiseresolved');
|
||||
return result;
|
||||
});
|
||||
|
||||
return Promise.all([func_promise, ...event_promises])
|
||||
.then((result) => {
|
||||
if (should_be_first !== order[0]) {
|
||||
throw should_be_first === 'promiseresolved' ?
|
||||
`'${event}' was fired before promise resolved.` :
|
||||
`Promise resolved before '${event}' was fired.`;
|
||||
}
|
||||
|
||||
if (order[0] !== 'promiseresolved' &&
|
||||
order[order.length - 1] !== 'promiseresolved') {
|
||||
throw 'Promise resolved in between event listeners.';
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
// See assert_promise_event_order_ above.
|
||||
function assert_promise_resolves_before_event(
|
||||
object, func, event, num_listeners=1) {
|
||||
return assert_promise_event_order_(
|
||||
'promiseresolved', object, func, event, num_listeners);
|
||||
}
|
||||
|
||||
// See assert_promise_event_order_ above.
|
||||
function assert_promise_resolves_after_event(
|
||||
object, func, event, num_listeners=1) {
|
||||
return assert_promise_event_order_(
|
||||
'event', object, func, event, num_listeners);
|
||||
}
|
||||
|
||||
// Returns a promise that resolves after 100ms unless
|
||||
// the the event is fired on the object in which case
|
||||
// the promise rejects.
|
||||
function assert_no_events(object, event_name) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let event_listener = (e) => {
|
||||
object.removeEventListener(event_name, event_listener);
|
||||
assert_unreached('Object should not fire an event.');
|
||||
};
|
||||
object.addEventListener(event_name, event_listener);
|
||||
// TODO: Remove timeout.
|
||||
// http://crbug.com/543884
|
||||
step_timeout(() => {
|
||||
object.removeEventListener(event_name, event_listener);
|
||||
resolve();
|
||||
}, 100);
|
||||
});
|
||||
}
|
||||
|
||||
class TestCharacteristicProperties {
|
||||
// |properties| is an array of strings for property bits to be set
|
||||
// as true.
|
||||
constructor(properties) {
|
||||
this.broadcast = false;
|
||||
this.read = false;
|
||||
this.writeWithoutResponse = false;
|
||||
this.write = false;
|
||||
this.notify = false;
|
||||
this.indicate = false;
|
||||
this.authenticatedSignedWrites = false;
|
||||
this.reliableWrite = false;
|
||||
this.writableAuxiliaries = false;
|
||||
|
||||
properties.forEach(val => {
|
||||
if (this.hasOwnProperty(val))
|
||||
this[val] = true;
|
||||
else
|
||||
throw `Invalid member '${val}'`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function assert_properties_equal(properties, expected_properties) {
|
||||
for (let key in expected_properties) {
|
||||
assert_equals(properties[key], expected_properties[key]);
|
||||
}
|
||||
}
|
||||
|
||||
class EventCatcher {
|
||||
constructor(object, event) {
|
||||
this.eventFired = false;
|
||||
let event_listener = () => {
|
||||
object.removeEventListener(event, event_listener);
|
||||
this.eventFired = true;
|
||||
};
|
||||
object.addEventListener(event, event_listener);
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a function that when called returns a promise that resolves when
|
||||
// the device has disconnected. Example:
|
||||
// device.gatt.connect()
|
||||
// .then(gatt => get_request_disconnection(gatt))
|
||||
// .then(requestDisconnection => requestDisconnection())
|
||||
// .then(() => // device is now disconnected)
|
||||
function get_request_disconnection(gattServer) {
|
||||
return gattServer.getPrimaryService(request_disconnection_service_uuid)
|
||||
.then(service => service.getCharacteristic(request_disconnection_characteristic_uuid))
|
||||
.then(characteristic => {
|
||||
return () => assert_promise_rejects_with_message(
|
||||
characteristic.writeValue(new Uint8Array([0])),
|
||||
new DOMException(
|
||||
'GATT Server is disconnected. Cannot perform GATT operations. ' +
|
||||
'(Re)connect first with `device.gatt.connect`.',
|
||||
'NetworkError'));
|
||||
});
|
||||
}
|
||||
|
||||
function generateRequestDeviceArgsWithServices(services = ['heart_rate']) {
|
||||
return [{
|
||||
filters: [{ services: services }]
|
||||
}, {
|
||||
filters: [{ services: services, name: 'Name' }]
|
||||
}, {
|
||||
filters: [{ services: services, namePrefix: 'Pre' }]
|
||||
}, {
|
||||
filters: [{ services: services, name: 'Name', namePrefix: 'Pre' }]
|
||||
}, {
|
||||
filters: [{ services: services }],
|
||||
optionalServices: ['heart_rate']
|
||||
}, {
|
||||
filters: [{ services: services, name: 'Name' }],
|
||||
optionalServices: ['heart_rate']
|
||||
}, {
|
||||
filters: [{ services: services, namePrefix: 'Pre' }],
|
||||
optionalServices: ['heart_rate']
|
||||
}, {
|
||||
filters: [{ services: services, name: 'Name', namePrefix: 'Pre' }],
|
||||
optionalServices: ['heart_rate']
|
||||
}];
|
||||
}
|
||||
|
||||
// Simulates a pre-connected device with |address|, |name| and
|
||||
// |knownServiceUUIDs|.
|
||||
function setUpPreconnectedDevice({
|
||||
address = '00:00:00:00:00:00', name = 'LE Device', knownServiceUUIDs = []}) {
|
||||
return navigator.bluetooth.test.simulateCentral({state: 'powered-on'})
|
||||
.then(fake_central => fake_central.simulatePreconnectedPeripheral({
|
||||
address: address,
|
||||
name: name,
|
||||
knownServiceUUIDs: knownServiceUUIDs,
|
||||
}));
|
||||
}
|
||||
|
||||
// Returns a FakePeripheral that corresponds to a simulated pre-connected device
|
||||
// called 'Health Thermometer'. The device has two known serviceUUIDs:
|
||||
// 'generic_access' and 'health_thermometer'.
|
||||
function setUpHealthThermometerDevice() {
|
||||
return setUpPreconnectedDevice({
|
||||
address: '09:09:09:09:09:09',
|
||||
name: 'Health Thermometer',
|
||||
knownServiceUUIDs: ['generic_access', 'health_thermometer'],
|
||||
});
|
||||
}
|
||||
|
||||
// Returns an array containing two FakePeripherals corresponding
|
||||
// to the simulated devices.
|
||||
function setUpHealthThermometerAndHeartRateDevices() {
|
||||
return navigator.bluetooth.test.simulateCentral({state: 'powered-on'})
|
||||
.then(fake_central => Promise.all([
|
||||
fake_central.simulatePreconnectedPeripheral({
|
||||
address: '09:09:09:09:09:09',
|
||||
name: 'Health Thermometer',
|
||||
knownServiceUUIDs: ['generic_access', 'health_thermometer'],
|
||||
}),
|
||||
fake_central.simulatePreconnectedPeripheral({
|
||||
address: '08:08:08:08:08:08',
|
||||
name: 'Heart Rate',
|
||||
knownServiceUUIDs: ['generic_access', 'heart_rate'],
|
||||
})]));
|
||||
}
|
||||
|
||||
// Returns the same fake peripheral as setUpHealthThermometerDevice() except
|
||||
// that connecting to the peripheral will succeed.
|
||||
function setUpConnectableHealthThermometerDevice() {
|
||||
let fake_peripheral;
|
||||
return setUpHealthThermometerDevice()
|
||||
.then(_ => fake_peripheral = _)
|
||||
.then(() => fake_peripheral.setNextGATTConnectionResponse({
|
||||
code: HCI_SUCCESS,
|
||||
}))
|
||||
.then(() => fake_peripheral);
|
||||
}
|
||||
|
||||
// Returns an object containing a BluetoothDevice discovered using |options|,
|
||||
// its corresponding FakePeripheral and FakeRemoteGATTServices.
|
||||
// The simulated device is called 'Health Thermometer' it has two known service
|
||||
// UUIDs: 'generic_access' and 'health_thermometer' which correspond to two
|
||||
// services with the same UUIDs. The 'health thermometer' service contains three
|
||||
// characteristics:
|
||||
// - 'temperature_measurement' (indicate),
|
||||
// - 'temperature_type' (read),
|
||||
// - 'measurement_interval' (read, write, indicate)
|
||||
// The 'measurement_interval' characteristic contains a
|
||||
// 'gatt.client_characteristic_configuration' descriptor and a
|
||||
// 'characteristic_user_description' descriptor.
|
||||
// The device has been connected to and its attributes are ready to be
|
||||
// discovered.
|
||||
function getHealthThermometerDevice(options) {
|
||||
let result;
|
||||
return getConnectedHealthThermometerDevice(options)
|
||||
.then(_ => result = _)
|
||||
.then(() => result.fake_peripheral.setNextGATTDiscoveryResponse({
|
||||
code: HCI_SUCCESS,
|
||||
}))
|
||||
.then(() => result);
|
||||
}
|
||||
|
||||
// Similar to getHealthThermometerDevice except that the peripheral has
|
||||
// two 'health_thermometer' services.
|
||||
function getTwoHealthThermometerServicesDevice(options) {
|
||||
let device;
|
||||
let fake_peripheral;
|
||||
let fake_generic_access;
|
||||
let fake_health_thermometer1;
|
||||
let fake_health_thermometer2;
|
||||
|
||||
return getConnectedHealthThermometerDevice(options)
|
||||
.then(result => {
|
||||
({
|
||||
device,
|
||||
fake_peripheral,
|
||||
fake_generic_access,
|
||||
fake_health_thermometer: fake_health_thermometer1,
|
||||
} = result);
|
||||
})
|
||||
.then(() => fake_peripheral.addFakeService({uuid: 'health_thermometer'}))
|
||||
.then(s => fake_health_thermometer2 = s)
|
||||
.then(() => fake_peripheral.setNextGATTDiscoveryResponse({
|
||||
code: HCI_SUCCESS}))
|
||||
.then(() => ({
|
||||
device: device,
|
||||
fake_peripheral: fake_peripheral,
|
||||
fake_generic_access: fake_generic_access,
|
||||
fake_health_thermometer1: fake_health_thermometer1,
|
||||
fake_health_thermometer2: fake_health_thermometer2
|
||||
}));
|
||||
}
|
||||
|
||||
// Returns an object containing a Health Thermometer BluetoothRemoteGattService
|
||||
// and its corresponding FakeRemoteGATTService.
|
||||
function getHealthThermometerService() {
|
||||
let result;
|
||||
return getHealthThermometerDevice()
|
||||
.then(r => result = r)
|
||||
.then(() => result.device.gatt.getPrimaryService('health_thermometer'))
|
||||
.then(service => Object.assign(result, {
|
||||
service,
|
||||
fake_service: result.fake_health_thermometer,
|
||||
}));
|
||||
}
|
||||
|
||||
// Returns an object containing a Measurement Interval
|
||||
// BluetoothRemoteGATTCharacteristic and its corresponding
|
||||
// FakeRemoteGATTCharacteristic.
|
||||
function getMeasurementIntervalCharacteristic() {
|
||||
let result;
|
||||
return getHealthThermometerService()
|
||||
.then(r => result = r)
|
||||
.then(() => result.service.getCharacteristic('measurement_interval'))
|
||||
.then(characteristic => Object.assign(result, {
|
||||
characteristic,
|
||||
fake_characteristic: result.fake_measurement_interval,
|
||||
}));
|
||||
}
|
||||
|
||||
function getUserDescriptionDescriptor() {
|
||||
let result;
|
||||
return getMeasurementIntervalCharacteristic()
|
||||
.then(r => result = r)
|
||||
.then(() => result.characteristic.getDescriptor(
|
||||
'gatt.characteristic_user_description'))
|
||||
.then(descriptor => Object.assign(result, {
|
||||
descriptor,
|
||||
fake_descriptor: result.fake_user_description,
|
||||
}));
|
||||
}
|
||||
|
||||
// Populates a fake_peripheral with various fakes appropriate for a health
|
||||
// thermometer. This resolves to an associative array composed of the fakes,
|
||||
// including the |fake_peripheral|.
|
||||
function populateHealthThermometerFakes(fake_peripheral) {
|
||||
let fake_generic_access, fake_health_thermometer, fake_measurement_interval,
|
||||
fake_user_description, fake_cccd, fake_temperature_measurement,
|
||||
fake_temperature_type;
|
||||
return fake_peripheral.addFakeService({uuid: 'generic_access'})
|
||||
.then(_ => fake_generic_access = _)
|
||||
.then(() => fake_peripheral.addFakeService({
|
||||
uuid: 'health_thermometer',
|
||||
}))
|
||||
.then(_ => fake_health_thermometer = _)
|
||||
.then(() => fake_health_thermometer.addFakeCharacteristic({
|
||||
uuid: 'measurement_interval',
|
||||
properties: ['read', 'write', 'indicate'],
|
||||
}))
|
||||
.then(_ => fake_measurement_interval = _)
|
||||
.then(() => fake_measurement_interval.addFakeDescriptor({
|
||||
uuid: 'gatt.characteristic_user_description',
|
||||
}))
|
||||
.then(_ => fake_user_description = _)
|
||||
.then(() => fake_measurement_interval.addFakeDescriptor({
|
||||
uuid: 'gatt.client_characteristic_configuration',
|
||||
}))
|
||||
.then(_ => fake_cccd = _)
|
||||
.then(() => fake_health_thermometer.addFakeCharacteristic({
|
||||
uuid: 'temperature_measurement',
|
||||
properties: ['indicate'],
|
||||
}))
|
||||
.then(_ => fake_temperature_measurement = _)
|
||||
.then(() => fake_health_thermometer.addFakeCharacteristic({
|
||||
uuid: 'temperature_type',
|
||||
properties: ['read'],
|
||||
}))
|
||||
.then(_ => fake_temperature_type = _)
|
||||
.then(() => ({
|
||||
fake_peripheral,
|
||||
fake_generic_access,
|
||||
fake_health_thermometer,
|
||||
fake_measurement_interval,
|
||||
fake_cccd,
|
||||
fake_user_description,
|
||||
fake_temperature_measurement,
|
||||
fake_temperature_type,
|
||||
}));
|
||||
}
|
||||
|
||||
// Similar to getHealthThermometerDevice except the GATT discovery
|
||||
// response has not been set yet so more attributes can still be added.
|
||||
function getConnectedHealthThermometerDevice(options) {
|
||||
let device, fake_peripheral, fakes;
|
||||
return getDiscoveredHealthThermometerDevice(options)
|
||||
.then(_ => ({device, fake_peripheral} = _))
|
||||
.then(() => fake_peripheral.setNextGATTConnectionResponse({
|
||||
code: HCI_SUCCESS,
|
||||
}))
|
||||
.then(() => populateHealthThermometerFakes(fake_peripheral))
|
||||
.then(_ => fakes = _)
|
||||
.then(() => device.gatt.connect())
|
||||
.then(() => Object.assign({device}, fakes));
|
||||
}
|
||||
|
||||
// Returns the same device and fake peripheral as getHealthThermometerDevice()
|
||||
// after another frame (an iframe we insert) discovered the device,
|
||||
// connected to it and discovered its services.
|
||||
function getHealthThermometerDeviceWithServicesDiscovered(options) {
|
||||
let device, fake_peripheral, fakes;
|
||||
let iframe = document.createElement('iframe');
|
||||
return setUpConnectableHealthThermometerDevice()
|
||||
.then(_ => fake_peripheral = _)
|
||||
.then(() => populateHealthThermometerFakes(fake_peripheral))
|
||||
.then(_ => fakes = _)
|
||||
.then(() => fake_peripheral.setNextGATTDiscoveryResponse({
|
||||
code: HCI_SUCCESS,
|
||||
}))
|
||||
.then(() => new Promise(resolve => {
|
||||
iframe.src = '../../../resources/bluetooth/health-thermometer-iframe.html';
|
||||
document.body.appendChild(iframe);
|
||||
iframe.addEventListener('load', resolve);
|
||||
}))
|
||||
.then(() => new Promise((resolve, reject) => {
|
||||
callWithTrustedClick(() => {
|
||||
iframe.contentWindow.postMessage({
|
||||
type: 'DiscoverServices',
|
||||
options: options
|
||||
}, '*');
|
||||
});
|
||||
|
||||
function messageHandler(messageEvent) {
|
||||
if (messageEvent.data == 'DiscoveryComplete') {
|
||||
window.removeEventListener('message', messageHandler);
|
||||
resolve();
|
||||
} else {
|
||||
reject(new Error(`Unexpected message: ${messageEvent.data}`));
|
||||
}
|
||||
}
|
||||
window.addEventListener('message', messageHandler);
|
||||
}))
|
||||
.then(() => requestDeviceWithTrustedClick(options))
|
||||
.then(_ => device = _)
|
||||
.then(device => device.gatt.connect())
|
||||
.then(_ => Object.assign({device}, fakes));
|
||||
}
|
||||
|
||||
// Similar to getHealthThermometerDevice() except the device has no services,
|
||||
// characteristics, or descriptors.
|
||||
function getEmptyHealthThermometerDevice(options) {
|
||||
return getDiscoveredHealthThermometerDevice(options)
|
||||
.then(({device, fake_peripheral}) => {
|
||||
return fake_peripheral.setNextGATTConnectionResponse({code: HCI_SUCCESS})
|
||||
.then(() => device.gatt.connect())
|
||||
.then(() => fake_peripheral.setNextGATTDiscoveryResponse({
|
||||
code: HCI_SUCCESS}))
|
||||
.then(() => ({
|
||||
device: device,
|
||||
fake_peripheral: fake_peripheral
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// Similar to getHealthThermometerService() except the service has no
|
||||
// characteristics or included services.
|
||||
function getEmptyHealthThermometerService(options) {
|
||||
let device;
|
||||
let fake_peripheral;
|
||||
let fake_health_thermometer;
|
||||
return getDiscoveredHealthThermometerDevice(options)
|
||||
.then(result => ({device, fake_peripheral} = result))
|
||||
.then(() => fake_peripheral.setNextGATTConnectionResponse({
|
||||
code: HCI_SUCCESS}))
|
||||
.then(() => device.gatt.connect())
|
||||
.then(() => fake_peripheral.addFakeService({uuid: 'health_thermometer'}))
|
||||
.then(s => fake_health_thermometer = s)
|
||||
.then(() => fake_peripheral.setNextGATTDiscoveryResponse({
|
||||
code: HCI_SUCCESS}))
|
||||
.then(() => device.gatt.getPrimaryService('health_thermometer'))
|
||||
.then(service => ({
|
||||
service: service,
|
||||
fake_health_thermometer: fake_health_thermometer,
|
||||
}));
|
||||
}
|
||||
|
||||
// Returns a BluetoothDevice discovered using |options| and its
|
||||
// corresponding FakePeripheral.
|
||||
// The simulated device is called 'HID Device' it has three known service
|
||||
// UUIDs: 'generic_access', 'device_information', 'human_interface_device'.
|
||||
// The primary service with 'device_information' UUID has a characteristics
|
||||
// with UUID 'serial_number_string'. The device has been connected to and its
|
||||
// attributes are ready to be discovered.
|
||||
// TODO(crbug.com/719816): Add descriptors.
|
||||
function getHIDDevice(options) {
|
||||
return setUpPreconnectedDevice({
|
||||
address: '10:10:10:10:10:10',
|
||||
name: 'HID Device',
|
||||
knownServiceUUIDs: [
|
||||
'generic_access',
|
||||
'device_information',
|
||||
'human_interface_device'
|
||||
],
|
||||
})
|
||||
.then(fake_peripheral => {
|
||||
return requestDeviceWithTrustedClick(options)
|
||||
.then(device => {
|
||||
return fake_peripheral
|
||||
.setNextGATTConnectionResponse({
|
||||
code: HCI_SUCCESS})
|
||||
.then(() => device.gatt.connect())
|
||||
.then(() => fake_peripheral.addFakeService({
|
||||
uuid: 'generic_access'}))
|
||||
.then(() => fake_peripheral.addFakeService({
|
||||
uuid: 'device_information'}))
|
||||
// Blocklisted Characteristic:
|
||||
// https://github.com/WebBluetoothCG/registries/blob/master/gatt_blocklist.txt
|
||||
.then(dev_info => dev_info.addFakeCharacteristic({
|
||||
uuid: 'serial_number_string', properties: ['read']}))
|
||||
.then(() => fake_peripheral.addFakeService({
|
||||
uuid: 'human_interface_device'}))
|
||||
.then(() => fake_peripheral.setNextGATTDiscoveryResponse({
|
||||
code: HCI_SUCCESS}))
|
||||
.then(() => ({
|
||||
device: device,
|
||||
fake_peripheral: fake_peripheral
|
||||
}));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Similar to getHealthThermometerDevice() except the device
|
||||
// is not connected and thus its services have not been
|
||||
// discovered.
|
||||
function getDiscoveredHealthThermometerDevice(
|
||||
options = {filters: [{services: ['health_thermometer']}]}) {
|
||||
return setUpHealthThermometerDevice()
|
||||
.then(fake_peripheral => {
|
||||
return requestDeviceWithTrustedClick(options)
|
||||
.then(device => ({
|
||||
device: device,
|
||||
fake_peripheral: fake_peripheral
|
||||
}));
|
||||
});
|
||||
}
|
|
@ -33,3 +33,14 @@ function getAudioURI(base)
|
|||
|
||||
return base + extension;
|
||||
}
|
||||
|
||||
function getMediaContentType(url) {
|
||||
var extension = new URL(url, location).pathname.split(".").pop();
|
||||
var map = {
|
||||
"mp4": "video/mp4",
|
||||
"ogv": "video/ogg",
|
||||
"mp3": "audio/mp3",
|
||||
"oga": "audio/ogg",
|
||||
};
|
||||
return map[extension];
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
<title>ARIA Tree Example</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="./css/treesimple.css" type="text/css">
|
||||
<script type="text/javascript" src="./js/prototype.js"></script>
|
||||
<script type="text/javascript" src="./js/aria.js"></script>
|
||||
<script type="text/javascript" src="./js/init.js"></script>
|
||||
<script src="./js/prototype.js"></script>
|
||||
<script src="./js/aria.js"></script>
|
||||
<script src="./js/init.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
<title>ARIA Tree Example</title>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="./css/treebox.css" type="text/css">
|
||||
<script type="text/javascript" src="./js/prototype.js"></script>
|
||||
<script type="text/javascript" src="./js/aria.js"></script>
|
||||
<script type="text/javascript" src="./js/init.js"></script>
|
||||
<script src="./js/prototype.js"></script>
|
||||
<script src="./js/aria.js"></script>
|
||||
<script src="./js/init.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
After the onload event completes a child DOM text node is added to the element with
|
||||
the aria-live attribute.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addTextNode() {
|
||||
var node = document.getElementById('TEST_ID');
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
After the onload event completes a child DOM text node is added to the element
|
||||
with the aria-live attribute.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addTextNode() {
|
||||
var node = document.getElementById('TEST_ID');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
value 'assertive' is added to the element and a child DOM text
|
||||
node is added to the element.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addLiveRegion() {
|
||||
var node = document.getElementById('TEST_ID');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
value 'assertive' is added to the document and a child element and text
|
||||
node is added to the element with the aria-live attribute.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addElement() {
|
||||
var node = document.getElementById('TEST_ID');
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
After the onload event completes a child DOM element node with text content has the
|
||||
CSS dsiplay property changed to display="block".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID');
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
a child DOM element node with text content has the CSS dsiplay property
|
||||
changed to display="block".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID');
|
||||
|
|
|
@ -20,7 +20,7 @@
|
|||
After the onload event completes a child DOM element node with text content has the
|
||||
CSS dsiplay property changed to visibility="visible".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID');
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
After the onload event completes a child DOM element node with text content has the CSS dsiplay
|
||||
property changed to visibility="visible".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID');
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
event with the value="polite". After the onload event completes a child DOM text node is added
|
||||
to the element with the aria-live attribute.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addChildTextNode() {
|
||||
var node = document.getElementById('TEST_ID');
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
event with the value="assertive". After the onload event completes a child DOM text node is
|
||||
added to the element with the aria-live attribute.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addChildTextNode() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
event with the value="polite". After the onload event completes a child DOM element node is
|
||||
added to the element with the aria-live attribute.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addChildElementNode() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
the onload event with the value="assertive". After the onload event completes a
|
||||
child DOM element node is added to the element with the aria-live attribute.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addChildElementNode() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
that is hidden using CSS display="none". After the onload event completes a child DOM element
|
||||
node with text content has the CSS display property changed to display="block".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID_3');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
that is hidden using CSS display="none". After the onload event completes a child DOM element
|
||||
node with text content has the CSS display property changed to display="block".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID_3');
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
event completes a child DOM element node with text content has the CSS display property
|
||||
changed to visibility="visible".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID_3');
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
After the onload event completes a child DOM element node with text content
|
||||
has the CSS display property changed to visibility="visible".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID_3');
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
onload event completes with the value="polite". After the aria-live attribute is added,
|
||||
a child DOM text node is added to the element with the aria-live attribute.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addChildTextNode() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
the onload event completes with the value="assertive". After the aria-live attribute
|
||||
is added, a child DOM text node is added to the element with the aria-live attribute.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addChildTextNode() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
attribute is added, a child DOM element node is added to the element with
|
||||
the aria-live attribute.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addChildElementNode() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
the onload event completes with the value="assertive". After the aria-live attribute
|
||||
is added, a child DOM element node is added to the element with the aria-live attribute.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function addChildElementNode() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
aria-live attribute is added, the child DOM element node with text content has the
|
||||
CSS display property changed to display="block".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
the aria-live attribute is added, the child DOM element node with text content has
|
||||
the CSS display property changed to display="block".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID_3');
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
visibility="hidden". After the aria-live attribute is added, the child DOM
|
||||
element node with text content has the CSS display property changed to visibility="visible".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID_3');
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
the aria-live attribute is added, the child DOM element node with text content has
|
||||
the CSS display property changed to visibility="visible".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function showElement() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
has a child DOM text node with content. After the onload event completes the child
|
||||
DOM text node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function deleteChildNodes() {
|
||||
var node = document.getElementById('TEST_ID_1');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
has a child DOM text node with content. After the onload event completes the child DOM
|
||||
text node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function deleteChildNodes() {
|
||||
var node = document.getElementById('TEST_ID_1');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
has a child DOM element node that contains text content. After the onload event completes
|
||||
the child DOM element node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function deleteChildNodes() {
|
||||
var node = document.getElementById('TEST_ID_1');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
value="assertive" has a child DOM element node that contains text content.
|
||||
After the onload event completes the child DOM element node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function deleteChildNodes() {
|
||||
var node = document.getElementById('TEST_ID_1');
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
content. After the onload event completes the CSS 'display' property
|
||||
of the child DOM element node is changed to display="none".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function hideElement() {
|
||||
var node = document.getElementById('TEST_ID_1');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
has a child DOM element node that contains text content. After the onload event completes
|
||||
the CSS 'display' property of the child DOM element node is changed to display="none".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function hideElement() {
|
||||
var node = document.getElementById('TEST_ID_1');
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
completes the CSS 'visibility' property of the child DOM element node is changed
|
||||
to visibility="hidden".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function hideElement() {
|
||||
var node = document.getElementById('TEST_ID_1');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
has a child DOM element node that contains text content. After the onload event completes
|
||||
the CSS 'visibility' property of the child DOM element node is changed to visibility="hidden".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function hideElement() {
|
||||
var node = document.getElementById('TEST_ID_1');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
text node with content. After the onload event completes the child DOM text
|
||||
node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function deleteChildNodes() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
event with the value="assertive" and the element has a child DOM text node with content. After
|
||||
the onload event completes the child DOM text node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function deleteChildNodes() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
DOM element node that contains text content. After the onload event completes
|
||||
the child DOM element node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function deleteChildNodes() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
node that contains text content. After the onload event completes the child DOM
|
||||
element node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
|
||||
function deleteChildNodes() {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
that contains text content. After the onload event completes the CSS 'display'
|
||||
property of the child DOM element node is changed to display="none".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
|
||||
function hideElement() {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
node that contains text content. After the onload event completes the CSS 'display'
|
||||
property of the child DOM element node is changed to display="none".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
|
||||
function hideElement() {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
the CSS 'visibility' property of the child DOM element node is changed
|
||||
to visibility="hidden".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
|
||||
function hideElement() {
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
event completes the CSS 'visibility' property of the child DOM element
|
||||
node is changed to visibility="hidden".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
|
||||
function hideElement() {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
the element has a child DOM text node with content. After the
|
||||
aria-live attribute is added, the child DOM text node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
|
||||
function deleteChildNodes() {
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
and the element has a child DOM text node with content. After the
|
||||
aria-live attribute is added, the child DOM text node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function deleteChildNodes() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
a child DOM element node that contains text content. After the aria-live
|
||||
attribute is added, the child DOM element node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function deleteChildNodes() {
|
||||
var node = document.getElementById('TEST_ID_1');
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
a child DOM element node that contains text content. After the aria-live
|
||||
attribute is added, the child DOM element node is deleted.</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function deleteChildNodes() {
|
||||
var node = document.getElementById('TEST_ID_1');
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
attribute is added, the CSS 'display' property of the child DOM element
|
||||
node is changed to display="none".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function hideElement() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
attribute is added, the CSS 'display' property of the child DOM element
|
||||
node is changed to display="none".</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
<script>
|
||||
|
||||
function hideElement() {
|
||||
var node = document.getElementById('TEST_ID_2');
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче