diff --git a/.github/release.yml b/.github/release.yml index 8f13b55a4e290..95dee31ec9630 100644 --- a/.github/release.yml +++ b/.github/release.yml @@ -1,6 +1,6 @@ changelog: exclude: labels: - -dependencies + - dependencies authors: - selenium-ci diff --git a/.github/workflows/ci-python.yml b/.github/workflows/ci-python.yml index 9749fd4e7ad82..cebe68597d063 100644 --- a/.github/workflows/ci-python.yml +++ b/.github/workflows/ci-python.yml @@ -86,3 +86,15 @@ jobs: browser: firefox cache-key: py-remote run: bazel test --local_test_jobs 1 --flaky_test_attempts 3 //py:test-remote + + safari-tests: + name: Safari Tests + needs: build + uses: ./.github/workflows/bazel.yml + with: + name: Integration Tests (safari) + browser: safari + os: macos + cache-key: py-safari + run: | + bazel test --local_test_jobs 1 --flaky_test_attempts 3 //py:test-safari-test/selenium/webdriver/safari/launcher_tests.py diff --git a/.github/workflows/ci-rbe.yml b/.github/workflows/ci-rbe.yml index 382aea6409f2a..2e15949925580 100644 --- a/.github/workflows/ci-rbe.yml +++ b/.github/workflows/ci-rbe.yml @@ -15,7 +15,7 @@ jobs: with: name: Check format script run caching: false - ruby-version: jruby-9.4.5.0 + ruby-version: jruby-9.4.8.0 run: ./scripts/github-actions/check-format.sh test: @@ -25,5 +25,5 @@ jobs: with: name: All RBE tests caching: false - ruby-version: jruby-9.4.5.0 + ruby-version: jruby-9.4.8.0 run: ./scripts/github-actions/ci-build.sh diff --git a/.github/workflows/ci-ruby.yml b/.github/workflows/ci-ruby.yml index 419e17df5065c..88cf3512ebfba 100644 --- a/.github/workflows/ci-ruby.yml +++ b/.github/workflows/ci-ruby.yml @@ -39,17 +39,17 @@ jobs: fail-fast: false matrix: include: - - ruby-version: 3.0.6 + - ruby-version: 3.1.6 os: ubuntu - - ruby-version: 3.0.6 + - ruby-version: 3.1.6 os: windows - - ruby-version: 3.0.6 + - ruby-version: 3.1.6 os: macos - - ruby-version: 3.3.0 + - ruby-version: 3.3.5 os: ubuntu - - ruby-version: jruby-9.4.5.0 + - ruby-version: jruby-9.4.8.0 os: ubuntu - - ruby-version: truffleruby-23.1.1 + - ruby-version: truffleruby-24.1.1 os: ubuntu with: name: Unit Tests (${{ matrix.ruby-version }}, ${{ matrix.os }}) diff --git a/AUTHORS b/AUTHORS index 6ed08840399de..c5dd7ca1b1756 100644 --- a/AUTHORS +++ b/AUTHORS @@ -91,6 +91,7 @@ Ashley Trinh Aslak Hellesøy asmundak Atsushi Tatsuma +Augustin Gottlieb <33221555+aguspe@users.noreply.github.com> Augustin Gottlieb Pequeno <33221555+aguspe@users.noreply.github.com> Aurélien Pupier Austin Michael Wilkins <42476341+amwilkins@users.noreply.github.com> @@ -106,6 +107,7 @@ bgermann bhecquet bhkwan Bill Agee +BlitzDestroyer <143762104+BlitzDestroyer@users.noreply.github.com> bob Bob Baron Bob Lubecker @@ -199,6 +201,7 @@ Darrin Cherry Dave Hoover Dave Hunt daviande +David Bernhard David Burns David English David Fischer @@ -696,6 +699,7 @@ PombaM Potapov Dmitriy Prakhar Rawat praveendvd <45095911+praveendvd@users.noreply.github.com> +Priyansh Garg Puja Jagani Pulkit Sharma Pydi Chandra @@ -719,6 +723,7 @@ richard.hines RichCrook richseviora Rishav Trivedi +Rob Brackett Rob Richardson Rob Wu Robert Elliot diff --git a/MODULE.bazel b/MODULE.bazel index 3ab1515e9c3fb..b52df075394b5 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -25,7 +25,7 @@ bazel_dep(name = "rules_oci", version = "1.7.6") bazel_dep(name = "rules_pkg", version = "0.10.1") bazel_dep(name = "rules_python", version = "0.33.0") bazel_dep(name = "rules_proto", version = "6.0.0") -bazel_dep(name = "rules_ruby", version = "0.11.0") +bazel_dep(name = "rules_ruby", version = "0.13.0") linter = use_extension("@apple_rules_lint//lint:extensions.bzl", "linter") linter.configure( @@ -177,48 +177,48 @@ maven.install( "com.google.auto:auto-common:1.2.2", "com.google.auto.service:auto-service:1.1.1", "com.google.auto.service:auto-service-annotations:1.1.1", - "com.google.googlejavaformat:google-java-format:jar:1.23.0", + "com.google.googlejavaformat:google-java-format:jar:1.24.0", "com.graphql-java:graphql-java:22.3", "dev.failsafe:failsafe:3.3.2", - "io.grpc:grpc-context:1.66.0", + "io.grpc:grpc-context:1.68.0", "io.lettuce:lettuce-core:6.4.0.RELEASE", - "io.netty:netty-buffer:4.1.113.Final", - "io.netty:netty-codec-http:4.1.113.Final", - "io.netty:netty-codec-http2:4.1.113.Final", - "io.netty:netty-common:4.1.113.Final", - "io.netty:netty-handler:4.1.113.Final", - "io.netty:netty-handler-proxy:4.1.113.Final", - "io.netty:netty-transport:4.1.113.Final", - "io.opentelemetry:opentelemetry-api:1.42.1", - "io.opentelemetry:opentelemetry-context:1.42.1", - "io.opentelemetry:opentelemetry-exporter-logging:1.42.1", - "io.opentelemetry:opentelemetry-sdk:1.42.1", - "io.opentelemetry:opentelemetry-sdk-common:1.42.1", - "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.42.1", - "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.42.1", - "io.opentelemetry:opentelemetry-sdk-testing:1.42.1", - "io.opentelemetry:opentelemetry-sdk-trace:1.42.1", + "io.netty:netty-buffer:4.1.114.Final", + "io.netty:netty-codec-http:4.1.114.Final", + "io.netty:netty-codec-http2:4.1.114.Final", + "io.netty:netty-common:4.1.114.Final", + "io.netty:netty-handler:4.1.114.Final", + "io.netty:netty-handler-proxy:4.1.114.Final", + "io.netty:netty-transport:4.1.114.Final", + "io.opentelemetry:opentelemetry-api:1.43.0", + "io.opentelemetry:opentelemetry-context:1.43.0", + "io.opentelemetry:opentelemetry-exporter-logging:1.43.0", + "io.opentelemetry:opentelemetry-sdk:1.43.0", + "io.opentelemetry:opentelemetry-sdk-common:1.43.0", + "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure:1.43.0", + "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi:1.43.0", + "io.opentelemetry:opentelemetry-sdk-testing:1.43.0", + "io.opentelemetry:opentelemetry-sdk-trace:1.43.0", "io.opentelemetry.semconv:opentelemetry-semconv:1.25.0-alpha", "io.ous:jtoml:2.0.0", "it.ozimov:embedded-redis:0.7.3", - "net.bytebuddy:byte-buddy:1.15.1", - "org.htmlunit:htmlunit-core-js:4.4.0", + "net.bytebuddy:byte-buddy:1.15.7", + "org.htmlunit:htmlunit-core-js:4.5.0", "org.apache.commons:commons-exec:1.4.0", - "org.apache.logging.log4j:log4j-core:2.24.0", + "org.apache.logging.log4j:log4j-core:2.24.1", "org.assertj:assertj-core:3.26.3", "org.bouncycastle:bcpkix-jdk18on:1.78.1", "org.eclipse.mylyn.github:org.eclipse.egit.github.core:2.1.5", "org.hsqldb:hsqldb:2.7.3", "org.jspecify:jspecify:1.0.0", - "org.junit.jupiter:junit-jupiter-api:5.11.0", - "org.junit.jupiter:junit-jupiter-engine:5.11.0", - "org.junit.jupiter:junit-jupiter-params:5.11.0", - "org.junit.platform:junit-platform-launcher:1.11.0", - "org.junit.platform:junit-platform-reporting:1.11.0", - "org.junit.platform:junit-platform-commons:1.11.0", - "org.junit.platform:junit-platform-engine:1.11.0", - "org.mockito:mockito-core:5.13.0", - "org.redisson:redisson:3.36.0", + "org.junit.jupiter:junit-jupiter-api:5.11.3", + "org.junit.jupiter:junit-jupiter-engine:5.11.3", + "org.junit.jupiter:junit-jupiter-params:5.11.3", + "org.junit.platform:junit-platform-launcher:1.11.3", + "org.junit.platform:junit-platform-reporting:1.11.3", + "org.junit.platform:junit-platform-commons:1.11.3", + "org.junit.platform:junit-platform-engine:1.11.3", + "org.mockito:mockito-core:5.14.2", + "org.redisson:redisson:3.37.0", "org.slf4j:slf4j-api:2.0.16", "org.slf4j:slf4j-jdk14:2.0.16", "org.zeromq:jeromq:0.6.0", @@ -257,7 +257,7 @@ ruby.bundle_fetch( "//:rb/selenium-webdriver.gemspec", ], gem_checksums = { - "activesupport-7.2.1": "7557fa077a592a4f36f7ddacf4d9d71c34aff69ed20236b8a61c22d567da8c24", + "activesupport-7.2.1.2": "6c3f6ad50c4e52ce39d67aeda38f99e1372eca8b295987d072c19460ebce4cb1", "addressable-2.8.7": "462986537cf3735ab5f3c0f557f14155d778f4b43ea4f485a9deb9c8f7c58232", "ast-2.4.2": "1e280232e6a33754cde542bc5ef85520b74db2aac73ec14acef453784447cc12", "base64-0.2.0": "0f25e9b21a02a0cc0cea8ef92b2041035d39350946e8789c562b2d1a3da01507", @@ -267,7 +267,7 @@ ruby.bundle_fetch( "connection_pool-2.4.1": "0f40cf997091f1f04ff66da67eabd61a9fe0d4928b9a3645228532512fab62f4", "crack-1.0.0": "c83aefdb428cdc7b66c7f287e488c796f055c0839e6e545fec2c7047743c4a49", "csv-3.3.0": "0bbd1defdc31134abefed027a639b3723c2753862150f4c3ee61cab71b20d67d", - "curb-1.0.5": "2c4755dfb5d6190e9ebb4407b23ac5a5c2c226be1449e6d3bdf625656352efd1", + "curb-1.0.6": "b369434efa91dc7310d72a74f8a228a5b920e3d5a89b0a3097e4c6a905af6eb2", "debug-1.9.2": "48e026c0852c7a10c60263e2e527968308958e266231e36d64e3efcabec7e7fc", "diff-lcs-1.5.1": "273223dfb40685548436d32b4733aa67351769c7dea621da7d9dd4813e63ddfe", "drb-2.2.1": "e9d472bf785f558b96b25358bae115646da0dbfd45107ad858b0bc0d935cb340", @@ -277,45 +277,45 @@ ruby.bundle_fetch( "fileutils-1.7.2": "36a0fb324218263e52b486ad7408e9a295378fe8edc9fd343709e523c0980631", "git-1.19.1": "b0a422d9f6517353c48a330d6114de4db9e0c82dbe7202964a1d9f1fbc827d70", "hashdiff-1.1.1": "c7966316726e0ceefe9f5c6aef107ebc3ccfef8b6db55fe3934f046b2cf0936a", - "i18n-1.14.5": "26dcbc05e364b57e27ab430148b3377bc413987d34cc042336271d8f42e9d1b9", + "i18n-1.14.6": "dc229a74f5d181f09942dd60ab5d6e667f7392c4ee826f35096db36d1fe3614c", "io-console-0.7.2": "f0dccff252f877a4f60d04a4dc6b442b185ebffb4b320ab69212a92b48a7a221", "io-console-0.7.2-java": "73aa382f8832b116613ceaf57b8ff5bf73dfedcaf39f0aa5420e10f63a4543ed", - "irb-1.14.0": "53d805013bbd194874b8c13a56aca6aebcd11dd79166d88724f8a434fedde615", + "irb-1.14.1": "5975003b58d36efaf492380baa982ceedf5aed36967a4d5b40996bc5c66e80f8", "jar-dependencies-0.4.1": "b2df2f1ecbff15334ce20ea7fdd5b8d8161faab67761ff72c7647d728e40d387", - "json-2.7.2": "1898b5cbc81cd36c0fd4d0b7ad2682c39fb07c5ff682fc6265f678f550d4982c", - "json-2.7.2-java": "138e3038b5361b3d06ee2e8aa2be00bed0d0de4ef5f1553fc5935e5b93aca7ee", + "json-2.7.4": "9ea6258b4add3abd25df965515be8b19be417f58b8c42619c7f2d3e86c158ece", + "json-2.7.4-java": "5d5d1593d8727a66f2e4161710dde06ba7043c9b3fa9eea0889fdc0450a107cd", "language_server-protocol-3.17.0.3": "3d5c58c02f44a20d972957a9febe386d7e7468ab3900ce6bd2b563dd910c6b3f", "listen-3.9.0": "db9e4424e0e5834480385197c139cb6b0ae0ef28cc13310cfd1ca78377d59c67", - "logger-1.6.0": "0ab7c120262dd8de2a18cb8d377f1f318cbe98535160a508af9e7710ff43ef3e", + "logger-1.6.1": "3ad9587ed3940bf7897ea64a673971415523f4f7d6b22c5e3af5219705669653", "minitest-5.25.1": "3db6795a80634def1cf86fda79d2d83b59b25ce5e186fa675f73c565589d2ad8", "parallel-1.26.3": "d86babb7a2b814be9f4b81587bf0b6ce2da7d45969fab24d8ae4bf2bb4d4c7ef", - "parser-3.3.4.2": "71efa8690c2a1ff8937258ff5713add4894dcea8b4ba19055692e28a2469c9e6", + "parser-3.3.5.0": "f30ebb71b7830c2e7cdc4b2b0e0ec2234900e3fca3fe2fba47f78be759181ab3", "psych-5.1.2": "337322f58fc2bf24827d2b9bd5ab595f6a72971867d151bb39980060ea40a368", "psych-5.1.2-java": "1dd68dc609eddbc884e6892e11da942e16f7256bd30ebde9d35449d43043a6fe", "public_suffix-6.0.1": "61d44e1cab5cbbbe5b31068481cf16976dd0dc1b6b07bd95617ef8c5e3e00c6f", "racc-1.8.1": "4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f", "racc-1.8.1-java": "54f2e6d1e1b91c154013277d986f52a90e5ececbe91465d29172e49342732b98", - "rack-2.2.9": "fd6301a97a1c1e955e68f85c861fcb1cde6145a32c532e1ea321a72ff8cc4042", + "rack-2.2.10": "e4a5ee3f8f2ba45614a4498114d6dc7da1c51a0f0dd810d891906ea71d3aa72b", "rainbow-3.1.1": "039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a", "rake-13.2.1": "46cb38dae65d7d74b6020a4ac9d48afed8eb8149c040eccf0523bec91907059d", "rb-fsevent-0.11.2": "43900b972e7301d6570f64b850a5aa67833ee7d87b458ee92805d56b7318aefe", "rb-inotify-0.11.1": "a0a700441239b0ff18eb65e3866236cd78613d6b9f78fea1f9ac47a85e47be6e", - "rbs-3.5.3": "f262eea0db1e40eaa154266096b1a4272bc965a81d78acb0e54b58b4dc11f052", + "rbs-3.6.1": "ed7273d018556844583d1785ac54194e67eec594d68e317d57fa90ad035532c0", "rchardet-1.8.0": "693acd5253d5ade81a51940697955f6dd4bb2f0d245bda76a8e23deec70a52c7", "rdoc-6.7.0": "b17d5f0f57b0853d7b880d4360a32c7caf8dbb81f8503a36426df809e617f379", "regexp_parser-2.9.2": "5a27e767ad634f8a4b544520d5cd28a0db7aa1198a5d7c9d7e11d7b3d9066446", - "reline-0.5.9": "5d2dd7ed0fd078e79a05e4eaa47dc91b8dacec7358e9e1dd6d9c4636cff7d378", - "rexml-3.3.6": "7af0459d108dfd6ffa7d38c67c8464e200f5c9d476d4c6a3d1899e92808d63c6", + "reline-0.5.10": "1660c969a792ebd034e6ceee8ca628f3b6698dcdb34f7a282a5edda37b958166", + "rexml-3.3.9": "d71875b85299f341edf47d44df0212e7658cbdf35aeb69cefdb63f57af3137c9", "rspec-3.13.0": "d490914ac1d5a5a64a0e1400c1d54ddd2a501324d703b8cfe83f458337bab993", - "rspec-core-3.13.0": "557792b4e88da883d580342b263d9652b6a10a12d5bda9ef967b01a48f15454c", - "rspec-expectations-3.13.2": "565fb94ab39923c0fe6a16cfc9570d1821b741917a50800373fcbbb752c7a45a", - "rspec-mocks-3.13.1": "087189899c337937bcf1d66a50dc3fc999ac88335bbeba4d385c2a38c87d7b38", + "rspec-core-3.13.2": "94fbda6e4738e478f1c7532b7cc241272fcdc8b9eac03a97338b1122e4573300", + "rspec-expectations-3.13.3": "0e6b5af59b900147698ea0ff80456c4f2e69cac4394fbd392fbd1ca561f66c58", + "rspec-mocks-3.13.2": "2327335def0e1665325a9b617e3af9ae20272741d80ac550336309a7c59abdef", "rspec-support-3.13.1": "48877d4f15b772b7538f3693c22225f2eda490ba65a0515c4e7cf6f2f17de70f", - "rubocop-1.65.1": "3a239b71fcfdeb32c654f4b48c2e6aeb4f77b128e348fa9442184f207e70718d", - "rubocop-ast-1.32.1": "6a86ce3b7aa8ff8b600396d8f75ef5f85873b94bf0a1ae11607c19e65ced79c1", + "rubocop-1.67.0": "8ccca7226e76d0a9974af960ea446d1fb38adf0c491214294e2fed75a85c378c", + "rubocop-ast-1.32.3": "40201e861c73a3c2d59428c7627828ef81fb2f8a306bc4a1c1801452afe3fe0f", "rubocop-capybara-2.21.0": "5d264efdd8b6c7081a3d4889decf1451a1cfaaec204d81534e236bc825b280ab", "rubocop-factory_bot-2.26.1": "8de13cd4edcee5ca800f255188167ecef8dbfc3d1fae9f15734e9d2e755392aa", - "rubocop-performance-1.21.1": "5cf20002a544275ad6aa99abca4b945d2a2ed71be925c38fe83700360ed8734e", + "rubocop-performance-1.22.1": "9ed9737af1ee90655654b483e0eac4e64702139e85d33335bf744b57a309a679", "rubocop-rake-0.6.0": "56b6f22189af4b33d4f4e490a555c09f1281b02f4d48c3a61f6e8fe5f401d8db", "rubocop-rspec-2.31.0": "2bae19388d78e1ceace44cd95fd34f3209f4ef20cac1b168d0a1325cbba3d672", "rubocop-rspec_rails-2.29.1": "4ae95abbe9ca5a9b6d8be14e50d230fb5b6ba033b05d4c0981b5b76fc44988e4", @@ -328,11 +328,11 @@ ruby.bundle_fetch( "strscan-3.1.0-java": "8645aa76e017e21764c6df572d2d79fcc1672284014f5bdbd806278cdbcd11b0", "terminal-table-3.0.2": "f951b6af5f3e00203fb290a669e0a85c5dd5b051b3b023392ccfd67ba5abae91", "tzinfo-2.0.6": "8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b", - "unicode-display_width-2.5.0": "7e7681dcade1add70cb9fda20dd77f300b8587c81ebbd165d14fd93144ff0ab4", - "webmock-3.23.1": "0fa738c0767d1c4ec8cc57f6b21998f0c238c8a5b32450df1c847f2767140d95", - "webrick-1.8.1": "19411ec6912911fd3df13559110127ea2badd0c035f7762873f58afc803e158f", + "unicode-display_width-2.6.0": "12279874bba6d5e4d2728cef814b19197dbb10d7a7837a869bab65da943b7f5a", + "webmock-3.24.0": "be01357f6fc773606337ca79f3ba332b7d52cbe5c27587671abc0572dbec7122", + "webrick-1.8.2": "431746a349199546ff9dd272cae10849c865f938216e41c402a6489248f12f21", "websocket-1.2.11": "b7e7a74e2410b5e85c25858b26b3322f29161e300935f70a0e0d3c35e0462737", - "yard-0.9.36": "5505736c1b00c926f71053a606ab75f02070c5960d0778b901fe9d8b0a470be4", + "yard-0.9.37": "a6e910399e78e613f80ba9add9ba7c394b1a935f083cccbef82903a3d2a26992", }, gemfile = "//:rb/Gemfile", gemfile_lock = "//:rb/Gemfile.lock", diff --git a/Rakefile b/Rakefile index 57486c7976f03..24f41e429f1cd 100644 --- a/Rakefile +++ b/Rakefile @@ -99,7 +99,7 @@ JAVA_RELEASE_TARGETS = %w[ //java/src/org/openqa/selenium/chromium:chromium.publish //java/src/org/openqa/selenium/devtools/v128:v128.publish //java/src/org/openqa/selenium/devtools/v129:v129.publish - //java/src/org/openqa/selenium/devtools/v127:v127.publish + //java/src/org/openqa/selenium/devtools/v130:v130.publish //java/src/org/openqa/selenium/devtools/v85:v85.publish //java/src/org/openqa/selenium/edge:edge.publish //java/src/org/openqa/selenium/firefox:firefox.publish @@ -791,7 +791,7 @@ namespace :dotnet do sh 'docfx dotnet/docs/docfx.json' rescue StandardError case $CHILD_STATUS.exitstatus - when 127 + when 130 raise 'Ensure the dotnet/tools directory is added to your PATH environment variable (e.g., `~/.dotnet/tools`)' when 255 puts '.NET documentation build failed, likely because of DevTools namespacing. This is ok; continuing' diff --git a/common/devtools/chromium/v127/BUILD.bazel b/common/devtools/chromium/v130/BUILD.bazel similarity index 100% rename from common/devtools/chromium/v127/BUILD.bazel rename to common/devtools/chromium/v130/BUILD.bazel diff --git a/common/devtools/chromium/v127/browser_protocol.pdl b/common/devtools/chromium/v130/browser_protocol.pdl similarity index 97% rename from common/devtools/chromium/v127/browser_protocol.pdl rename to common/devtools/chromium/v130/browser_protocol.pdl index f1ac9eeb5d46c..8a9325e6f6528 100644 --- a/common/devtools/chromium/v127/browser_protocol.pdl +++ b/common/devtools/chromium/v130/browser_protocol.pdl @@ -156,6 +156,7 @@ experimental domain Accessibility flowto labelledby owns + url # A node in the accessibility tree. type AXNode extends object @@ -742,6 +743,7 @@ experimental domain Audits NoRegisterTriggerHeader NoRegisterOsSourceHeader NoRegisterOsTriggerHeader + NavigationRegistrationUniqueScopeAlreadySet type SharedDictionaryError extends string enum @@ -802,7 +804,6 @@ experimental domain Audits type GenericIssueErrorType extends string enum - CrossOriginPortalPostMessageError FormLabelForNameError FormDuplicateIdForInputError FormInputWithNoLabelError @@ -891,7 +892,9 @@ experimental domain Audits ClientMetadataNoResponse ClientMetadataInvalidResponse ClientMetadataInvalidContentType + IdpNotPotentiallyTrustworthy DisabledInSettings + DisabledInFlags ErrorFetchingSignin InvalidSigninResponse AccountsHttpNotFound @@ -914,6 +917,7 @@ experimental domain Audits NotSignedInWithIdp MissingTransientUserActivation ReplacedByButtonMode + InvalidFieldsSpecified RelyingPartyOriginIsOpaque TypeNotMatching @@ -1099,13 +1103,20 @@ experimental domain Audits parameters InspectorIssue issue -# Defines commands and events for browser extensions. Available if the client -# is connected using the --remote-debugging-pipe flag and -# the --enable-unsafe-extension-debugging flag is set. +# Defines commands and events for browser extensions. experimental domain Extensions + # Storage areas. + type StorageArea extends string + enum + session + local + sync + managed # Installs an unpacked extension from the filesystem similar to # --load-extension CLI flags. Returns extension ID once the extension - # has been installed. + # has been installed. Available if the client is connected using the + # --remote-debugging-pipe flag and the --enable-unsafe-extension-debugging + # flag is set. command loadUnpacked parameters # Absolute file path. @@ -1113,6 +1124,44 @@ experimental domain Extensions returns # Extension id. string id + # Gets data from extension storage in the given `storageArea`. If `keys` is + # specified, these are used to filter the result. + command getStorageItems + parameters + # ID of extension. + string id + # StorageArea to retrieve data from. + StorageArea storageArea + # Keys to retrieve. + optional array of string keys + returns + object data + # Removes `keys` from extension storage in the given `storageArea`. + command removeStorageItems + parameters + # ID of extension. + string id + # StorageArea to remove data from. + StorageArea storageArea + # Keys to remove. + array of string keys + # Clears extension storage in the given `storageArea`. + command clearStorageItems + parameters + # ID of extension. + string id + # StorageArea to remove data from. + StorageArea storageArea + # Sets `values` in extension storage in the given `storageArea`. The provided `values` + # will be merged with existing values in the storage area. + command setStorageItems + parameters + # ID of extension. + string id + # StorageArea to set data in. + StorageArea storageArea + # Values to set. + object values # Defines commands and events for Autofill. experimental domain Autofill @@ -1344,6 +1393,7 @@ domain Browser videoCapturePanTiltZoom wakeLockScreen wakeLockSystem + webAppInstallation windowManagement experimental type PermissionSetting extends string @@ -1366,6 +1416,8 @@ domain Browser optional boolean userVisibleOnly # For "clipboard" permission, may specify allowWithoutSanitization. optional boolean allowWithoutSanitization + # For "fullscreen" permission, must specify allowWithoutGesture:true. + optional boolean allowWithoutGesture # For "camera" permission, may specify panTiltZoom. optional boolean panTiltZoom @@ -2006,13 +2058,6 @@ experimental domain CSS # Associated style declaration. CSSStyle style - # CSS position-fallback rule representation. - deprecated type CSSPositionFallbackRule extends object - properties - Value name - # List of keyframes. - array of CSSTryRule tryRules - # CSS @position-try rule representation. type CSSPositionTryRule extends object properties @@ -2025,6 +2070,7 @@ experimental domain CSS StyleSheetOrigin origin # Associated style declaration. CSSStyle style + boolean active # CSS keyframes rule representation. type CSSKeyframesRule extends object @@ -2198,10 +2244,11 @@ experimental domain CSS optional array of InheritedPseudoElementMatches inheritedPseudoElements # A list of CSS keyframed animations matching this node. optional array of CSSKeyframesRule cssKeyframesRules - # A list of CSS position fallbacks matching this node. - deprecated optional array of CSSPositionFallbackRule cssPositionFallbackRules - # A list of CSS @position-try rules matching this node, based on the position-try-options property. + # A list of CSS @position-try rules matching this node, based on the position-try-fallbacks property. optional array of CSSPositionTryRule cssPositionTryRules + # Index of the active fallback in the applied position-try-fallback property, + # will not be set if there is no active position-try fallback. + optional integer activePositionFallbackIndex # A list of CSS at-property rules matching this node. optional array of CSSPropertyRule cssPropertyRules # A list of CSS property registrations matching this node. @@ -2632,6 +2679,7 @@ domain DOM after marker backdrop + column selection search-text target-text @@ -2641,6 +2689,8 @@ domain DOM first-line-inherited scroll-marker scroll-marker-group + scroll-next-button + scroll-prev-button scrollbar scrollbar-thumb scrollbar-button @@ -2654,6 +2704,12 @@ domain DOM view-transition-image-pair view-transition-old view-transition-new + placeholder + file-selector-button + details-content + select-fallback-button + select-fallback-button-text + picker # Shadow root type. type ShadowRootType extends string @@ -2758,6 +2814,13 @@ domain DOM optional boolean isSVG optional CompatibilityMode compatibilityMode optional BackendNode assignedSlot + experimental optional boolean isScrollable + + # A structure to hold the top-level node of a detached tree and an array of its retained descendants. + type DetachedElementInfo extends object + properties + Node treeNode + array of NodeId retainedNodeIds # A structure holding an RGBA color. type RGBA extends object @@ -3269,6 +3332,12 @@ domain DOM returns string path + # Returns list of detached nodes + experimental command getDetachedDomNodes + returns + # The list of detached nodes + array of DetachedElementInfo detachedNodes + # Enables console to refer to the node with given id via $x (see Command Line API for more details # $x functions). experimental command setInspectedNode @@ -3435,6 +3504,14 @@ domain DOM # Called when top layer elements are changed. experimental event topLayerElementsUpdated + # Fired when a node's scrollability state changes. + experimental event scrollableFlagUpdated + parameters + # The id of the node. + DOM.NodeId nodeId + # If the node is scrollable. + boolean isScrollable + # Called when a pseudo element is removed from an element. experimental event pseudoElementRemoved parameters @@ -4176,6 +4253,21 @@ domain Emulation optional SensorReadingXYZ xyz optional SensorReadingQuaternion quaternion + experimental type PressureSource extends string + enum + cpu + + experimental type PressureState extends string + enum + nominal + fair + serious + critical + + experimental type PressureMetadata extends object + properties + optional boolean available + # Tells whether emulation is supported. deprecated command canEmulate returns @@ -4345,6 +4437,24 @@ domain Emulation SensorType type SensorReading reading + # Overrides a pressure source of a given type, as used by the Compute + # Pressure API, so that updates to PressureObserver.observe() are provided + # via setPressureStateOverride instead of being retrieved from + # platform-provided telemetry data. + experimental command setPressureSourceOverrideEnabled + parameters + boolean enabled + PressureSource source + optional PressureMetadata metadata + + # Provides a given pressure state that will be processed and eventually be + # delivered to PressureObserver users. |source| must have been previously + # overridden by setPressureSourceOverrideEnabled. + experimental command setPressureStateOverride + parameters + PressureSource source + PressureState state + # Overrides the Idle state. command setIdleOverride parameters @@ -4553,6 +4663,42 @@ domain IO # UUID of the specified Blob. string uuid +experimental domain FileSystem + depends on Network + depends on Storage + + type File extends object + properties + string name + # Timestamp + Network.TimeSinceEpoch lastModified + # Size in bytes + number size + string type + + type Directory extends object + properties + string name + array of string nestedDirectories + # Files that are directly nested under this directory. + array of File nestedFiles + + type BucketFileSystemLocator extends object + properties + # Storage key + Storage.SerializedStorageKey storageKey + # Bucket name. Not passing a `bucketName` will retrieve the default Bucket. (https://developer.mozilla.org/en-US/docs/Web/API/Storage_API#storage_buckets) + optional string bucketName + # Path to the directory using each path component as an array item. + array of string pathComponents + + command getDirectory + parameters + BucketFileSystemLocator bucketFileSystemLocator + returns + # Returns the directory object at the path. + Directory directory + experimental domain IndexedDB depends on Runtime depends on Storage @@ -5394,12 +5540,21 @@ experimental domain Memory moderate critical + # Retruns current DOM object counters. command getDOMCounters returns integer documents integer nodes integer jsEventListeners + # Retruns DOM object counters after preparing renderer for leak detection. + command getDOMCountersForLeakDetection + returns + # DOM object counters. + array of DOMCounter counters + + # Prepares for leak detection by terminating workers, stopping spellcheckers, + # dropping non-essential internal caches, running garbage collections, etc. command prepareForLeakDetection # Simulate OomIntervention by purging V8 memory. @@ -5475,6 +5630,15 @@ experimental domain Memory # Size of the module in bytes. number size + # DOM object counter data. + type DOMCounter extends object + properties + # Object name. Note: object names should be presumed volatile and clients should not expect + # the returned names to be consistent across runs. + string name + # Object count. + integer count + # Network domain allows tracking network activities of the page. It exposes information about http, # file, data and other requests and responses, their headers, bodies, timing, etc. domain Network @@ -6212,6 +6376,8 @@ domain Network TPCDMetadata # The cookie should have been blocked by 3PCD but is exempted by Deprecation Trial mitigation. TPCDDeprecationTrial + # The cookie should have been blocked by 3PCD but is exempted by Top-level Deprecation Trial mitigation. + TopLevelTPCDDeprecationTrial # The cookie should have been blocked by 3PCD but is exempted by heuristics mitigation. TPCDHeuristics # The cookie should have been blocked by 3PCD but is exempted by Enterprise Policy. @@ -6220,8 +6386,6 @@ domain Network StorageAccess # The cookie should have been blocked by 3PCD but is exempted by Top-level Storage Access API. TopLevelStorageAccess - # The cookie should have been blocked by 3PCD but is exempted by CORS opt-in. - CorsOptIn # The cookie should have been blocked by 3PCD but is exempted by the first-party URL scheme. Scheme @@ -7118,6 +7282,9 @@ domain Network # The number of obtained Trust Tokens on a successful "Issuance" operation. optional integer issuedTokenCount + # Fired once security policy has been updated. + experimental event policyUpdated + # Fired once when parsing the .wbn file has succeeded. # The event contains the information about the web bundle contents. experimental event subresourceWebBundleMetadataReceived @@ -7170,6 +7337,7 @@ domain Network UnsafeNone SameOriginPlusCoep RestrictPropertiesPlusCoep + NoopenerAllowPopups experimental type CrossOriginOpenerPolicyStatus extends object properties @@ -7882,6 +8050,7 @@ domain Page experimental type PermissionsPolicyFeature extends string enum accelerometer + all-screens-capture ambient-light-sensor attribution-reporting autoplay @@ -7915,8 +8084,10 @@ domain Page clipboard-read clipboard-write compute-pressure + controlled-frame cross-origin-isolated deferred-fetch + digital-credentials-get direct-sockets display-capture document-domain @@ -7937,11 +8108,13 @@ domain Page keyboard-map local-fonts magnetometer + media-playback-while-not-visible microphone midi otp-credentials payment picture-in-picture + popins private-aggregation private-state-token-issuance private-state-token-redemption @@ -7962,6 +8135,7 @@ domain Page usb usb-unrestricted vertical-scroll + web-app-installation web-printing web-share window-management @@ -8274,14 +8448,16 @@ domain Page experimental type ClientNavigationReason extends string enum + anchorClick formSubmissionGet formSubmissionPost httpHeaderRefresh - scriptInitiated + initialFrameNavigation metaTagRefresh + other pageBlockInterstitial reload - anchorClick + scriptInitiated experimental type ClientNavigationDisposition extends string enum @@ -9059,6 +9235,13 @@ domain Page # A new frame target will be created (see Target.attachedToTarget). swap + # Fired before frame subtree is detached. Emitted before any frame of the + # subtree is actually detached. + experimental event frameSubtreeWillBeDetached + parameters + # Id of the frame that is the root of the subtree that will be detached. + FrameId frameId + # The type of a frameNavigated event. experimental type NavigationType extends string enum @@ -9284,7 +9467,6 @@ domain Page Printing WebDatabase PictureInPicture - Portal SpeechRecognizer IdleManager PaymentManager @@ -9317,6 +9499,7 @@ domain Page ContentWebUSB ContentMediaSessionService ContentScreenReader + ContentDiscarded # See components/back_forward_cache/back_forward_cache_disable.h for explanations. EmbedderPopupBlockerTabHelper @@ -9402,6 +9585,15 @@ domain Page FrameId frameId # Frame's new url. string url + # Navigation type + enum navigationType + # Navigation due to fragment navigation. + fragment + # Navigation due to history API usage. + historyApi + # Navigation due to other reasons. + other + # Compressed image data requested by the `startScreencast`. experimental event screencastFrame @@ -10474,6 +10666,31 @@ experimental domain Storage exact modulus + experimental type AttributionReportingAggregatableDebugReportingData extends object + properties + UnsignedInt128AsBase16 keyPiece + # number instead of integer because not all uint32 can be represented by + # int + number value + array of string types + + experimental type AttributionReportingAggregatableDebugReportingConfig extends object + properties + # number instead of integer because not all uint32 can be represented by + # int, only present for source registrations + optional number budget + UnsignedInt128AsBase16 keyPiece + array of AttributionReportingAggregatableDebugReportingData debugData + optional string aggregationCoordinatorOrigin + + experimental type AttributionScopesData extends object + properties + array of string values + # number instead of integer because not all uint32 can be represented by + # int + number limit + number maxEventStates + experimental type AttributionReportingSourceRegistration extends object properties Network.TimeSinceEpoch time @@ -10492,6 +10709,9 @@ experimental domain Storage array of AttributionReportingAggregationKeysEntry aggregationKeys optional UnsignedInt64AsBase10 debugKey AttributionReportingTriggerDataMatching triggerDataMatching + SignedInt64AsBase10 destinationLimitPriority + AttributionReportingAggregatableDebugReportingConfig aggregatableDebugReportingConfig + optional AttributionScopesData scopesData experimental type AttributionReportingSourceRegistrationResult extends string enum @@ -10507,7 +10727,10 @@ experimental domain Storage destinationBothLimitsReached reportingOriginsPerSiteLimitReached exceedsMaxChannelCapacity + exceedsMaxScopesChannelCapacity exceedsMaxTriggerStateCardinality + exceedsMaxEventStatesLimit + destinationPerDayReportingLimitReached experimental event attributionReportingSourceRegistered parameters @@ -10525,6 +10748,8 @@ experimental domain Storage # number instead of integer because not all uint32 can be represented by # int number value + UnsignedInt64AsBase10 filteringId + experimental type AttributionReportingAggregatableValueEntry extends object properties @@ -10557,10 +10782,13 @@ experimental domain Storage array of AttributionReportingEventTriggerData eventTriggerData array of AttributionReportingAggregatableTriggerData aggregatableTriggerData array of AttributionReportingAggregatableValueEntry aggregatableValues + integer aggregatableFilteringIdMaxBytes boolean debugReporting optional string aggregationCoordinatorOrigin AttributionReportingSourceRegistrationTimeConfig sourceRegistrationTimeConfig optional string triggerContextId + AttributionReportingAggregatableDebugReportingConfig aggregatableDebugReportingConfig + array of string scopes experimental type AttributionReportingEventLevelResult extends string enum @@ -10788,7 +11016,7 @@ domain Target experimental optional Page.FrameId openerFrameId experimental optional Browser.BrowserContextID browserContextId # Provides additional details for specific target types. For example, for - # the type of "page", this may be set to "portal" or "prerender". + # the type of "page", this may be set to "prerender". experimental optional string subtype # A filter used by target query/discovery/auto-attach operations. @@ -12176,6 +12404,9 @@ experimental domain Preload JavaScriptInterfaceAdded JavaScriptInterfaceRemoved AllPrerenderingCanceled + WindowClosed + SlowNetwork + OtherPrerenderedPageActivated # Fired when a preload enabled state is updated. event preloadEnabledStateUpdated @@ -12209,7 +12440,6 @@ experimental domain Preload PrefetchFailedMIMENotSupported PrefetchFailedNetError PrefetchFailedNon2XX - PrefetchFailedPerPageLimitExceeded PrefetchEvictedAfterCandidateRemoved PrefetchEvictedForNewerPrefetch PrefetchHeldback @@ -12340,7 +12570,7 @@ experimental domain FedCm parameters # Allows callers to disable the promise rejection delay that would # normally happen, if this is unimportant to what's being tested. - # (step 4 of https://w3c-fedid.github.io/FedCM/#browser-api-rp-sign-in) + # (step 4 of https://fedidcg.github.io/FedCM/#browser-api-rp-sign-in) optional boolean disableRejectionDelay command disable @@ -12416,7 +12646,7 @@ experimental domain PWA # manifestId. optional string installUrlOrBundleUrl - # Uninstals the given manifest_id and closes any opened app windows. + # Uninstalls the given manifest_id and closes any opened app windows. command uninstall parameters string manifestId @@ -12438,7 +12668,7 @@ experimental domain PWA # used to attach to via Target.attachToTarget or similar APIs. # If some files in the parameters cannot be handled by the web app, they will # be ignored. If none of the files can be handled, this API returns an error. - # If no files provided as the parameter, this API also returns an error. + # If no files are provided as the parameter, this API also returns an error. # # According to the definition of the file handlers in the manifest file, one # Target.TargetID may represent a page handling one or more files. The order @@ -12455,7 +12685,103 @@ experimental domain PWA # Opens the current page in its web app identified by the manifest id, needs # to be called on a page target. This function returns immediately without - # waiting for the app finishing loading. + # waiting for the app to finish loading. command openCurrentPageInApp parameters string manifestId + + # If user prefers opening the app in browser or an app window. + type DisplayMode extends string + enum + standalone + browser + + # Changes user settings of the web app identified by its manifestId. If the + # app was not installed, this command returns an error. Unset parameters will + # be ignored; unrecognized values will cause an error. + # + # Unlike the ones defined in the manifest files of the web apps, these + # settings are provided by the browser and controlled by the users, they + # impact the way the browser handling the web apps. + # + # See the comment of each parameter. + command changeAppUserSettings + parameters + string manifestId + # If user allows the links clicked on by the user in the app's scope, or + # extended scope if the manifest has scope extensions and the flags + # `DesktopPWAsLinkCapturingWithScopeExtensions` and + # `WebAppEnableScopeExtensions` are enabled. + # + # Note, the API does not support resetting the linkCapturing to the + # initial value, uninstalling and installing the web app again will reset + # it. + # + # TODO(crbug.com/339453269): Setting this value on ChromeOS is not + # supported yet. + optional boolean linkCapturing + optional DisplayMode displayMode + +# This domain allows configuring virtual Bluetooth devices to test +# the web-bluetooth API. +experimental domain BluetoothEmulation + # Indicates the various states of Central. + type CentralState extends string + enum + absent + powered-off + powered-on + + # Stores the manufacturer data + type ManufacturerData extends object + properties + # Company identifier + # https://bitbucket.org/bluetooth-SIG/public/src/main/assigned_numbers/company_identifiers/company_identifiers.yaml + # https://usb.org/developers + integer key + # Manufacturer-specific data + binary data + + # Stores the byte data of the advertisement packet sent by a Bluetooth device. + type ScanRecord extends object + properties + optional string name + optional array of string uuids + # Stores the external appearance description of the device. + optional integer appearance + # Stores the transmission power of a broadcasting device. + optional integer txPower + # Key is the company identifier and the value is an array of bytes of + # manufacturer specific data. + optional array of ManufacturerData manufacturerData + + # Stores the advertisement packet information that is sent by a Bluetooth device. + type ScanEntry extends object + properties + string deviceAddress + integer rssi + ScanRecord scanRecord + + # Enable the BluetoothEmulation domain. + command enable + parameters + # State of the simulated central. + CentralState state + + # Disable the BluetoothEmulation domain. + command disable + + # Simulates a peripheral with |address|, |name| and |knownServiceUuids| + # that has already been connected to the system. + command simulatePreconnectedPeripheral + parameters + string address + string name + array of ManufacturerData manufacturerData + array of string knownServiceUuids + + # Simulates an advertisement packet described in |entry| being received by + # the central. + command simulateAdvertisement + parameters + ScanEntry entry diff --git a/common/devtools/chromium/v127/js_protocol.pdl b/common/devtools/chromium/v130/js_protocol.pdl similarity index 100% rename from common/devtools/chromium/v127/js_protocol.pdl rename to common/devtools/chromium/v130/js_protocol.pdl diff --git a/common/mirror/selenium b/common/mirror/selenium index 5c1e9a15d6f24..664bf8d6e5a1b 100644 --- a/common/mirror/selenium +++ b/common/mirror/selenium @@ -3,13 +3,33 @@ "tag_name": "nightly", "assets": [ { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-java-4.26.0-SNAPSHOT.zip" + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-java-4.27.0-SNAPSHOT.zip" }, { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-server-4.26.0-SNAPSHOT.jar" + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-server-4.27.0-SNAPSHOT.jar" }, { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-server-4.26.0-SNAPSHOT.zip" + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-server-4.27.0-SNAPSHOT.zip" + } + ] + }, + { + "tag_name": "selenium-4.26.0", + "assets": [ + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.26.0/selenium-dotnet-4.26.0.zip" + }, + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.26.0/selenium-dotnet-strongnamed-4.26.0.zip" + }, + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.26.0/selenium-java-4.26.0.zip" + }, + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.26.0/selenium-server-4.26.0.jar" + }, + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.26.0/selenium-server-4.26.0.zip" } ] }, @@ -935,28 +955,5 @@ "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-rc-2/selenium-server-4.0.0-rc-2.zip" } ] - }, - { - "tag_name": "selenium-4.0.0-rc-1", - "assets": [ - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-rc-1/selenium-dotnet-4.0.0-rc1.zip" - }, - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-rc-1/selenium-dotnet-strongnamed-4.0.0-rc1.zip" - }, - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-rc-1/selenium-html-runner-4.0.0-rc-1.jar" - }, - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-rc-1/selenium-java-4.0.0-rc-1.zip" - }, - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-rc-1/selenium-server-4.0.0-rc-1.jar" - }, - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-rc-1/selenium-server-4.0.0-rc-1.zip" - } - ] } ] diff --git a/common/repositories.bzl b/common/repositories.bzl index 1a9e782144475..8498b0a000a6b 100644 --- a/common/repositories.bzl +++ b/common/repositories.bzl @@ -11,8 +11,8 @@ def pin_browsers(): http_archive( name = "linux_firefox", - url = "https://ftp.mozilla.org/pub/firefox/releases/131.0/linux-x86_64/en-US/firefox-131.0.tar.bz2", - sha256 = "4ca8504a62a31472ecb8c3a769d4301dd4ac692d4cc5d51b8fe2cf41e7b11106", + url = "https://ftp.mozilla.org/pub/firefox/releases/132.0/linux-x86_64/en-US/firefox-132.0.tar.bz2", + sha256 = "e3a6f9a68ac72f5df01fac8c97c6de1a353af4b350b8c8b49b2c26c1fbbb2538", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -33,8 +33,8 @@ js_library( dmg_archive( name = "mac_firefox", - url = "https://ftp.mozilla.org/pub/firefox/releases/131.0/mac/en-US/Firefox%20131.0.dmg", - sha256 = "cd243b44746f56ee2042572cccab2736c0c6d419f85f90ad163a4ba04979ccb2", + url = "https://ftp.mozilla.org/pub/firefox/releases/132.0/mac/en-US/Firefox%20132.0.dmg", + sha256 = "5924171ce774ba8d102ddb45c573ff8acd4e0c289b62597f941ca58d79289704", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -50,8 +50,8 @@ js_library( http_archive( name = "linux_beta_firefox", - url = "https://ftp.mozilla.org/pub/firefox/releases/132.0b2/linux-x86_64/en-US/firefox-132.0b2.tar.bz2", - sha256 = "f4fb251b50661f27a5f4d87a3dc2839ed329793e232aca47b146d84384208167", + url = "https://ftp.mozilla.org/pub/firefox/releases/133.0b2/linux-x86_64/en-US/firefox-133.0b2.tar.bz2", + sha256 = "8f6806367d338095a0d8fc67f92f7314d2f520b4fe9455fc94cf9a417aa1ecb4", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -72,8 +72,8 @@ js_library( dmg_archive( name = "mac_beta_firefox", - url = "https://ftp.mozilla.org/pub/firefox/releases/132.0b2/mac/en-US/Firefox%20132.0b2.dmg", - sha256 = "b7be772dc40c7065580e8556f7f037ae1f2960be0276c6f04ed453d4543712a1", + url = "https://ftp.mozilla.org/pub/firefox/releases/133.0b2/mac/en-US/Firefox%20133.0b2.dmg", + sha256 = "313a31899877a782c74b6f460a544407d828effb779c80c6f5ac8fca8e5cb872", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -123,10 +123,10 @@ js_library( pkg_archive( name = "mac_edge", - url = "https://msedge.sf.dl.delivery.mp.microsoft.com/filestreamingservice/files/1077769e-236a-4f55-801d-2f782881fe18/MicrosoftEdge-129.0.2792.65.pkg", - sha256 = "a89a37ddfd655c47a44401a157c6a903f04a87622b64faa86d3897f5506f7481", + url = "https://msedge.sf.dl.delivery.mp.microsoft.com/filestreamingservice/files/4e27a0ec-f389-48c0-a4a4-3f1993c5a461/MicrosoftEdge-130.0.2849.56.pkg", + sha256 = "e33831f893e659a3f40f496ea62be85a65e4f085268df9425352a2d3067287a8", move = { - "MicrosoftEdge-129.0.2792.65.pkg/Payload/Microsoft Edge.app": "Edge.app", + "MicrosoftEdge-130.0.2849.56.pkg/Payload/Microsoft Edge.app": "Edge.app", }, build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") @@ -143,8 +143,8 @@ js_library( deb_archive( name = "linux_edge", - url = "https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/microsoft-edge-stable_129.0.2792.65-1_amd64.deb", - sha256 = "c6e0ad1e9b44d821b86a263b82edac596dca9b8a9f07413f2ff3c100221a28e7", + url = "https://packages.microsoft.com/repos/edge/pool/main/m/microsoft-edge-stable/microsoft-edge-stable_130.0.2849.56-1_amd64.deb", + sha256 = "fb1231f65c43a22b399c0aed30c827d67c0a8b075c1a6cc2cd1353ebcc660adb", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -165,8 +165,8 @@ js_library( http_archive( name = "linux_edgedriver", - url = "https://msedgedriver.azureedge.net/129.0.2792.65/edgedriver_linux64.zip", - sha256 = "36811ca6700532a98c27f696f79aa06762d882408645e6d6a09f3ed2673a6951", + url = "https://msedgedriver.azureedge.net/130.0.2849.68/edgedriver_linux64.zip", + sha256 = "fd831ca9def2061be07e496d66c1b3ff8882b8c061615dc7f5122ffee05ee013", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -182,8 +182,8 @@ js_library( http_archive( name = "mac_edgedriver", - url = "https://msedgedriver.azureedge.net/129.0.2792.65/edgedriver_mac64.zip", - sha256 = "53d7dd53564cf20aaf51d505b364f28d30a83d38b87a08d6bf22d26e04d17c2b", + url = "https://msedgedriver.azureedge.net/130.0.2849.68/edgedriver_mac64.zip", + sha256 = "0320730b275a89afeca1aebedf98f2cea47422dbfd47f643f2fd968f84832dc5", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -199,8 +199,8 @@ js_library( http_archive( name = "linux_chrome", - url = "https://storage.googleapis.com/chrome-for-testing-public/129.0.6668.89/linux64/chrome-linux64.zip", - sha256 = "fd61ba345c840c6d5404d8abdb42c5d86832b212e9bc401a72dc86495c0cd7d3", + url = "https://storage.googleapis.com/chrome-for-testing-public/130.0.6723.91/linux64/chrome-linux64.zip", + sha256 = "9190cc0540c9f59df5e81aae48d0e048dca6f7343266cee17d956931d844b1e7", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") package(default_visibility = ["//visibility:public"]) @@ -221,8 +221,8 @@ js_library( http_archive( name = "mac_chrome", - url = "https://storage.googleapis.com/chrome-for-testing-public/129.0.6668.89/mac-x64/chrome-mac-x64.zip", - sha256 = "dd67b65ed1eac994032b02d48c6aaeef7c0a20ba0450bf124e2fb558a1ab4a1b", + url = "https://storage.googleapis.com/chrome-for-testing-public/130.0.6723.91/mac-x64/chrome-mac-x64.zip", + sha256 = "1706e6f0671a78fa75d9567577c38e7d6848da9144c83ccba80fc840d2347cd3", strip_prefix = "chrome-mac-x64", patch_cmds = [ "mv 'Google Chrome for Testing.app' Chrome.app", @@ -243,8 +243,8 @@ js_library( http_archive( name = "linux_chromedriver", - url = "https://storage.googleapis.com/chrome-for-testing-public/129.0.6668.89/linux64/chromedriver-linux64.zip", - sha256 = "0dafb169734d3fc79171cabcecb07131bfb6a1727859118b756a020211b6c804", + url = "https://storage.googleapis.com/chrome-for-testing-public/130.0.6723.91/linux64/chromedriver-linux64.zip", + sha256 = "a8c94cea296c22a9bc1b928b138b8655bfecd9b372652c909b1b2af841ca5ff7", strip_prefix = "chromedriver-linux64", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") @@ -261,8 +261,8 @@ js_library( http_archive( name = "mac_chromedriver", - url = "https://storage.googleapis.com/chrome-for-testing-public/129.0.6668.89/mac-x64/chromedriver-mac-x64.zip", - sha256 = "71663c3f70fe3eb2256c474c60fa7ee1c15065f38b6c679e6c2d46960e774b1e", + url = "https://storage.googleapis.com/chrome-for-testing-public/130.0.6723.91/mac-x64/chromedriver-mac-x64.zip", + sha256 = "e2e137e1ff7d7d3886e4526ad319c4a1da90bcd4f601af247ac60ec58e38c570", strip_prefix = "chromedriver-mac-x64", build_file_content = """ load("@aspect_rules_js//js:defs.bzl", "js_library") diff --git a/common/selenium_manager.bzl b/common/selenium_manager.bzl index bb0133c12e949..50f890f1e2029 100644 --- a/common/selenium_manager.bzl +++ b/common/selenium_manager.bzl @@ -6,22 +6,22 @@ def selenium_manager(): http_file( name = "download_sm_linux", executable = True, - sha256 = "d4d775c38f5403d4a719e69903e6f70d15d2454d03da80ad6b82515a4ebfb986", - url = "https://github.com/SeleniumHQ/selenium_manager_artifacts/releases/download/selenium-manager-dffb534/selenium-manager-linux", + sha256 = "97ab346b907a813c236f1c6b9eb0e1b878702374b0768894415629c2cf05d97e", + url = "https://github.com/SeleniumHQ/selenium_manager_artifacts/releases/download/selenium-manager-33ad1b2/selenium-manager-linux", ) http_file( name = "download_sm_macos", executable = True, - sha256 = "2d6b20c603c4ca913423b3725cdc7ffa7e6a1554c9c161e3da226b186ba71054", - url = "https://github.com/SeleniumHQ/selenium_manager_artifacts/releases/download/selenium-manager-dffb534/selenium-manager-macos", + sha256 = "ef27b5c2d274dc4ab4417334116a1530571edc3deaf4740068e35484e275f28a", + url = "https://github.com/SeleniumHQ/selenium_manager_artifacts/releases/download/selenium-manager-33ad1b2/selenium-manager-macos", ) http_file( name = "download_sm_windows", executable = True, - sha256 = "58c47a131fd4323c647a95cb37baeafc5a14a536885ccc152457e87a4fd2188d", - url = "https://github.com/SeleniumHQ/selenium_manager_artifacts/releases/download/selenium-manager-dffb534/selenium-manager-windows.exe", + sha256 = "15113137d8d0d3648be9948c52e56e1f4c605bc5d9623962991198e8d0d413b6", + url = "https://github.com/SeleniumHQ/selenium_manager_artifacts/releases/download/selenium-manager-33ad1b2/selenium-manager-windows.exe", ) def _selenium_manager_artifacts_impl(_ctx): diff --git a/dotnet/CHANGELOG b/dotnet/CHANGELOG index a237ec48e7f02..ba87fcdaa96da 100644 --- a/dotnet/CHANGELOG +++ b/dotnet/CHANGELOG @@ -1,3 +1,22 @@ +v4.26.1 +====== +* [bidi] Reveal browsing context module in bidi instance (#14684) +* Fix adding cookies when ReturnedCookie class is used (#14697) + +v4.26.0 +====== +* [bidi] Fix web socket communication for .net framework +* Don't include http headers in internal logs (#14546) +* Don't write trace log message when waiting until driver service is initialized (#14557) +* Support GetLog command by Remote Web Driver (#14549) +* Add more internal logs around CDP DevTools communication (#14558) +* Interactions with Selenium Manager are AOT compatible (#14481) +* Allow setting of PageDimensions and PageMargins in PrintOptions directly (#14593) +* Fix devtools check in `NetworkManager` to determine CDP is supported (#14638) +* Lazy-load Selenium manager binary location (#14639) +* [bidi] Second round of BiDi implementation (#14566) +* Add CDP for Chrome 130 and remove 127 + v4.25.0 ====== * Add CDP for Chrome 129 and remove 126 diff --git a/dotnet/selenium-dotnet-version.bzl b/dotnet/selenium-dotnet-version.bzl index aad327babbc0d..eb9c52953feee 100644 --- a/dotnet/selenium-dotnet-version.bzl +++ b/dotnet/selenium-dotnet-version.bzl @@ -1,6 +1,6 @@ # BUILD FILE SYNTAX: STARLARK -SE_VERSION = "4.26.0-nightly202409202352" +SE_VERSION = "4.27.0-nightly202410311906" ASSEMBLY_VERSION = "4.0.0.0" SUPPORTED_NET_STANDARD_VERSIONS = ["netstandard2.0"] @@ -8,7 +8,7 @@ SUPPORTED_DEVTOOLS_VERSIONS = [ "v85", "v128", "v129", - "v127", + "v130", ] ASSEMBLY_COMPANY = "Selenium Committers" diff --git a/dotnet/src/webdriver/BiDi/BiDi.cs b/dotnet/src/webdriver/BiDi/BiDi.cs index db53571e8ad8c..0e0ed10c37036 100644 --- a/dotnet/src/webdriver/BiDi/BiDi.cs +++ b/dotnet/src/webdriver/BiDi/BiDi.cs @@ -1,9 +1,10 @@ using System; -using System.Collections.Generic; using System.Threading.Tasks; using OpenQA.Selenium.BiDi.Communication; using OpenQA.Selenium.BiDi.Communication.Transport; +#nullable enable + namespace OpenQA.Selenium.BiDi; public class BiDi : IAsyncDisposable @@ -38,11 +39,11 @@ internal BiDi(string url) } internal Modules.Session.SessionModule SessionModule => _sessionModule.Value; - internal Modules.BrowsingContext.BrowsingContextModule BrowsingContextModule => _browsingContextModule.Value; + public Modules.BrowsingContext.BrowsingContextModule BrowsingContext => _browsingContextModule.Value; public Modules.Browser.BrowserModule Browser => _browserModule.Value; public Modules.Network.NetworkModule Network => _networkModule.Value; internal Modules.Input.InputModule InputModule => _inputModule.Value; - internal Modules.Script.ScriptModule ScriptModule => _scriptModule.Value; + public Modules.Script.ScriptModule Script => _scriptModule.Value; public Modules.Log.LogModule Log => _logModule.Value; public Modules.Storage.StorageModule Storage => _storageModule.Value; @@ -60,16 +61,6 @@ public static async Task ConnectAsync(string url) return bidi; } - public Task CreateContextAsync(Modules.BrowsingContext.ContextType type, Modules.BrowsingContext.CreateOptions? options = null) - { - return BrowsingContextModule.CreateAsync(type, options); - } - - public Task> GetTreeAsync(Modules.BrowsingContext.GetTreeOptions? options = null) - { - return BrowsingContextModule.GetTreeAsync(options); - } - public Task EndAsync(Modules.Session.EndOptions? options = null) { return SessionModule.EndAsync(options); @@ -81,44 +72,4 @@ public async ValueTask DisposeAsync() _transport?.Dispose(); } - - public Task OnContextCreatedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) - { - return BrowsingContextModule.OnContextCreatedAsync(handler, options); - } - - public Task OnContextCreatedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) - { - return BrowsingContextModule.OnContextCreatedAsync(handler, options); - } - - public Task OnContextDestroyedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) - { - return BrowsingContextModule.OnContextDestroyedAsync(handler, options); - } - - public Task OnContextDestroyedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) - { - return BrowsingContextModule.OnContextDestroyedAsync(handler, options); - } - - public Task OnUserPromptOpenedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) - { - return BrowsingContextModule.OnUserPromptOpenedAsync(handler, options); - } - - public Task OnUserPromptOpenedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) - { - return BrowsingContextModule.OnUserPromptOpenedAsync(handler, options); - } - - public Task OnUserPromptClosedAsync(Func handler, BrowsingContextsSubscriptionOptions? options = null) - { - return BrowsingContextModule.OnUserPromptClosedAsync(handler, options); - } - - public Task OnUserPromptClosedAsync(Action handler, BrowsingContextsSubscriptionOptions? options = null) - { - return BrowsingContextModule.OnUserPromptClosedAsync(handler, options); - } } diff --git a/dotnet/src/webdriver/BiDi/BiDiException.cs b/dotnet/src/webdriver/BiDi/BiDiException.cs index 7f2ddd5b59cbc..6959645111b01 100644 --- a/dotnet/src/webdriver/BiDi/BiDiException.cs +++ b/dotnet/src/webdriver/BiDi/BiDiException.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace OpenQA.Selenium.BiDi; public class BiDiException : Exception diff --git a/dotnet/src/webdriver/BiDi/Communication/Broker.cs b/dotnet/src/webdriver/BiDi/Communication/Broker.cs index 82549d1d5c5b1..5f8a0983846c9 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Broker.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Broker.cs @@ -10,6 +10,8 @@ using System.Threading; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication; public class Broker : IAsyncDisposable @@ -34,7 +36,7 @@ public class Broker : IAsyncDisposable private readonly JsonSerializerOptions _jsonSerializerOptions; - public Broker(BiDi bidi, ITransport transport) + internal Broker(BiDi bidi, ITransport transport) { _bidi = bidi; _transport = transport; @@ -51,7 +53,7 @@ public Broker(BiDi bidi, ITransport transport) new NavigationConverter(), new InterceptConverter(_bidi), new RequestConverter(_bidi), - new ChannelConverter(_bidi), + new ChannelConverter(), new HandleConverter(_bidi), new InternalIdConverter(_bidi), new PreloadScriptConverter(_bidi), @@ -59,6 +61,7 @@ public Broker(BiDi bidi, ITransport transport) new RealmTypeConverter(), new DateTimeOffsetConverter(), new PrintPageRangeConverter(), + new InputOriginConverter(), new JsonStringEnumConverter(JsonNamingPolicy.CamelCase), // https://github.com/dotnet/runtime/issues/72604 @@ -68,6 +71,13 @@ public Broker(BiDi bidi, ITransport transport) new Json.Converters.Polymorphic.RealmInfoConverter(), new Json.Converters.Polymorphic.LogEntryConverter(), // + + // Enumerable + new Json.Converters.Enumerable.GetCookiesResultConverter(), + new Json.Converters.Enumerable.LocateNodesResultConverter(), + new Json.Converters.Enumerable.InputSourceActionsConverter(), + new Json.Converters.Enumerable.GetUserContextsResultConverter(), + new Json.Converters.Enumerable.GetRealmsResultConverter(), } }; } diff --git a/dotnet/src/webdriver/BiDi/Communication/Command.cs b/dotnet/src/webdriver/BiDi/Communication/Command.cs index bfa7113512068..729b319cc03ec 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Command.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Command.cs @@ -1,5 +1,7 @@ using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication; [JsonPolymorphic(TypeDiscriminatorPropertyName = "method")] diff --git a/dotnet/src/webdriver/BiDi/Communication/CommandOptions.cs b/dotnet/src/webdriver/BiDi/Communication/CommandOptions.cs index 93cc63fc2ebfc..ffb6616ae9cef 100644 --- a/dotnet/src/webdriver/BiDi/Communication/CommandOptions.cs +++ b/dotnet/src/webdriver/BiDi/Communication/CommandOptions.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication; public record CommandOptions diff --git a/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs b/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs index 4aab8620e3f2b..ce5444eb0d9fd 100644 --- a/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs +++ b/dotnet/src/webdriver/BiDi/Communication/EventHandler.cs @@ -3,6 +3,8 @@ using System.Collections.Generic; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication; public abstract class EventHandler(string eventName, Type eventArgsType, IEnumerable? contexts = null) diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/BrowserUserContextConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/BrowserUserContextConverter.cs index 7997f7c0f1541..3d478cf5d9f69 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/BrowserUserContextConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/BrowserUserContextConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class BrowserUserContextConverter : JsonConverter diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/BrowsingContextConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/BrowsingContextConverter.cs index 2718700f3ffac..8c1a928ef19ad 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/BrowsingContextConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/BrowsingContextConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class BrowsingContextConverter : JsonConverter diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/ChannelConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/ChannelConverter.cs index c2a7da0accffd..ac35a22fcc2c4 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/ChannelConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/ChannelConverter.cs @@ -3,22 +3,17 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class ChannelConverter : JsonConverter { - private readonly BiDi _bidi; - - public ChannelConverter(BiDi bidi) - { - _bidi = bidi; - } - public override Channel? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var id = reader.GetString(); - return new Channel(_bidi, id!); + return new Channel(id!); } public override void Write(Utf8JsonWriter writer, Channel value, JsonSerializerOptions options) diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/DateTimeOffsetConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/DateTimeOffsetConverter.cs index 9d87f0f17342e..ff9b6258d3a69 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/DateTimeOffsetConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/DateTimeOffsetConverter.cs @@ -2,6 +2,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class DateTimeOffsetConverter : JsonConverter diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/GetCookiesResultConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/GetCookiesResultConverter.cs new file mode 100644 index 0000000000000..bd92fa36eb119 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/GetCookiesResultConverter.cs @@ -0,0 +1,26 @@ +using OpenQA.Selenium.BiDi.Modules.Storage; +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +#nullable enable + +namespace OpenQA.Selenium.BiDi.Communication.Json.Converters.Enumerable; + +internal class GetCookiesResultConverter : JsonConverter +{ + public override GetCookiesResult Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var doc = JsonDocument.ParseValue(ref reader); + var cookies = doc.RootElement.GetProperty("cookies").Deserialize>(options); + var partitionKey = doc.RootElement.GetProperty("partitionKey").Deserialize(options); + + return new GetCookiesResult(cookies!, partitionKey!); + } + + public override void Write(Utf8JsonWriter writer, GetCookiesResult value, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } +} diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/GetRealmsResultConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/GetRealmsResultConverter.cs new file mode 100644 index 0000000000000..34c5a979c02c6 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/GetRealmsResultConverter.cs @@ -0,0 +1,25 @@ +using OpenQA.Selenium.BiDi.Modules.Script; +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +#nullable enable + +namespace OpenQA.Selenium.BiDi.Communication.Json.Converters.Enumerable; + +internal class GetRealmsResultConverter : JsonConverter +{ + public override GetRealmsResult Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var doc = JsonDocument.ParseValue(ref reader); + var realms = doc.RootElement.GetProperty("realms").Deserialize>(options); + + return new GetRealmsResult(realms!); + } + + public override void Write(Utf8JsonWriter writer, GetRealmsResult value, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } +} diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/GetUserContextsResultConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/GetUserContextsResultConverter.cs new file mode 100644 index 0000000000000..123c415c9bcd1 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/GetUserContextsResultConverter.cs @@ -0,0 +1,25 @@ +using OpenQA.Selenium.BiDi.Modules.Browser; +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +#nullable enable + +namespace OpenQA.Selenium.BiDi.Communication.Json.Converters.Enumerable; + +internal class GetUserContextsResultConverter : JsonConverter +{ + public override GetUserContextsResult Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var doc = JsonDocument.ParseValue(ref reader); + var userContexts = doc.RootElement.GetProperty("userContexts").Deserialize>(options); + + return new GetUserContextsResult(userContexts!); + } + + public override void Write(Utf8JsonWriter writer, GetUserContextsResult value, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } +} diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/InputSourceActionsConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/InputSourceActionsConverter.cs new file mode 100644 index 0000000000000..4e4c72c91fbe9 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/InputSourceActionsConverter.cs @@ -0,0 +1,61 @@ +using OpenQA.Selenium.BiDi.Modules.Input; +using System; +using System.Linq; +using System.Text.Json; +using System.Text.Json.Serialization; + +#nullable enable + +namespace OpenQA.Selenium.BiDi.Communication.Json.Converters.Enumerable; + +internal class InputSourceActionsConverter : JsonConverter +{ + public override SourceActions Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, SourceActions value, JsonSerializerOptions options) + { + writer.WriteStartObject(); + + writer.WriteString("id", value.Id); + + switch (value) + { + case KeyActions keys: + writer.WriteString("type", "key"); + writer.WritePropertyName("actions"); + JsonSerializer.Serialize(writer, keys.Actions.Select(a => a as IKeySourceAction), options); + + break; + case PointerActions pointers: + writer.WriteString("type", "pointer"); + if (pointers.Options is not null) + { + writer.WritePropertyName("parameters"); + JsonSerializer.Serialize(writer, pointers.Options, options); + } + + writer.WritePropertyName("actions"); + JsonSerializer.Serialize(writer, pointers.Actions.Select(a => a as IPointerSourceAction), options); + + break; + case WheelActions wheels: + writer.WriteString("type", "wheel"); + writer.WritePropertyName("actions"); + JsonSerializer.Serialize(writer, wheels.Actions.Select(a => a as IWheelSourceAction), options); + + break; + case NoneActions none: + writer.WriteString("type", "none"); + writer.WritePropertyName("actions"); + JsonSerializer.Serialize(writer, none.Actions.Select(a => a as INoneSourceAction), options); + + break; + } + + writer.WriteEndObject(); + } +} + diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/LocateNodesResultConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/LocateNodesResultConverter.cs new file mode 100644 index 0000000000000..307433c7be023 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Enumerable/LocateNodesResultConverter.cs @@ -0,0 +1,26 @@ +using OpenQA.Selenium.BiDi.Modules.BrowsingContext; +using OpenQA.Selenium.BiDi.Modules.Script; +using System; +using System.Collections.Generic; +using System.Text.Json; +using System.Text.Json.Serialization; + +#nullable enable + +namespace OpenQA.Selenium.BiDi.Communication.Json.Converters.Enumerable; + +internal class LocateNodesResultConverter : JsonConverter +{ + public override LocateNodesResult Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var doc = JsonDocument.ParseValue(ref reader); + var nodes = doc.RootElement.GetProperty("nodes").Deserialize>(options); + + return new LocateNodesResult(nodes!); + } + + public override void Write(Utf8JsonWriter writer, LocateNodesResult value, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } +} diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/HandleConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/HandleConverter.cs index 71e0ed6e5ae51..2117bb72fa519 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/HandleConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/HandleConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class HandleConverter : JsonConverter diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/InputOriginConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/InputOriginConverter.cs new file mode 100644 index 0000000000000..c544362ccccd8 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/InputOriginConverter.cs @@ -0,0 +1,36 @@ +using OpenQA.Selenium.BiDi.Modules.Input; +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +#nullable enable + +namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; + +internal class InputOriginConverter : JsonConverter +{ + public override Origin Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + throw new NotImplementedException(); + } + + public override void Write(Utf8JsonWriter writer, Origin value, JsonSerializerOptions options) + { + if (value is Origin.Viewport) + { + writer.WriteStringValue("viewport"); + } + else if (value is Origin.Pointer) + { + writer.WriteStringValue("pointer"); + } + else if (value is Origin.Element element) + { + writer.WriteStartObject(); + writer.WriteString("type", "element"); + writer.WritePropertyName("element"); + JsonSerializer.Serialize(writer, element.SharedReference, options); + writer.WriteEndObject(); + } + } +} diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/InterceptConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/InterceptConverter.cs index 3a207a89116c2..3fd062b22fae3 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/InterceptConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/InterceptConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class InterceptConverter : JsonConverter diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/InternalIdConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/InternalIdConverter.cs index 9271a1877be71..7edcaa7543464 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/InternalIdConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/InternalIdConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class InternalIdConverter : JsonConverter diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/NavigationConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/NavigationConverter.cs index 040e7fb723919..e61d300f18f56 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/NavigationConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/NavigationConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class NavigationConverter : JsonConverter diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/EvaluateResultConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/EvaluateResultConverter.cs index c349d82f90f04..ac0fd1a7bbfcc 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/EvaluateResultConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/EvaluateResultConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters.Polymorphic; // https://github.com/dotnet/runtime/issues/72604 @@ -14,8 +16,8 @@ internal class EvaluateResultConverter : JsonConverter return jsonDocument.RootElement.GetProperty("type").ToString() switch { - "success" => jsonDocument.Deserialize(options), - "exception" => jsonDocument.Deserialize(options), + "success" => jsonDocument.Deserialize(options), + "exception" => jsonDocument.Deserialize(options), _ => null, }; } diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/LogEntryConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/LogEntryConverter.cs index ada10ab054a50..e5ccbac46f326 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/LogEntryConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/LogEntryConverter.cs @@ -3,24 +3,26 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters.Polymorphic; // https://github.com/dotnet/runtime/issues/72604 -internal class LogEntryConverter : JsonConverter +internal class LogEntryConverter : JsonConverter { - public override BaseLogEntry? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override Modules.Log.Entry? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var jsonDocument = JsonDocument.ParseValue(ref reader); return jsonDocument.RootElement.GetProperty("type").ToString() switch { - "console" => jsonDocument.Deserialize(options), - "javascript" => jsonDocument.Deserialize(options), + "console" => jsonDocument.Deserialize(options), + "javascript" => jsonDocument.Deserialize(options), _ => null, }; } - public override void Write(Utf8JsonWriter writer, BaseLogEntry value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, Modules.Log.Entry value, JsonSerializerOptions options) { throw new NotImplementedException(); } diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/MessageConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/MessageConverter.cs index 6164dcc04690d..b9d86ae634902 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/MessageConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/MessageConverter.cs @@ -2,6 +2,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters.Polymorphic; // https://github.com/dotnet/runtime/issues/72604 diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/RealmInfoConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/RealmInfoConverter.cs index b3d061783dfae..9824377ca47ac 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/RealmInfoConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/RealmInfoConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters.Polymorphic; // https://github.com/dotnet/runtime/issues/72604 @@ -14,14 +16,14 @@ internal class RealmInfoConverter : JsonConverter return jsonDocument.RootElement.GetProperty("type").ToString() switch { - "window" => jsonDocument.Deserialize(options), - "dedicated-worker" => jsonDocument.Deserialize(options), - "shared-worker" => jsonDocument.Deserialize(options), - "service-worker" => jsonDocument.Deserialize(options), - "worker" => jsonDocument.Deserialize(options), - "paint-worklet" => jsonDocument.Deserialize(options), - "audio-worklet" => jsonDocument.Deserialize(options), - "worklet" => jsonDocument.Deserialize(options), + "window" => jsonDocument.Deserialize(options), + "dedicated-worker" => jsonDocument.Deserialize(options), + "shared-worker" => jsonDocument.Deserialize(options), + "service-worker" => jsonDocument.Deserialize(options), + "worker" => jsonDocument.Deserialize(options), + "paint-worklet" => jsonDocument.Deserialize(options), + "audio-worklet" => jsonDocument.Deserialize(options), + "worklet" => jsonDocument.Deserialize(options), _ => null, }; } diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/RemoteValueConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/RemoteValueConverter.cs index 1d3b08211b348..3f19d2a18c56c 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/RemoteValueConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/Polymorphic/RemoteValueConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters.Polymorphic; // https://github.com/dotnet/runtime/issues/72604 @@ -12,31 +14,38 @@ internal class RemoteValueConverter : JsonConverter { var jsonDocument = JsonDocument.ParseValue(ref reader); + if (jsonDocument.RootElement.ValueKind == JsonValueKind.String) + { + return new RemoteValue.String(jsonDocument.RootElement.GetString()!); + } + return jsonDocument.RootElement.GetProperty("type").ToString() switch { - "number" => jsonDocument.Deserialize(options), - "string" => jsonDocument.Deserialize(options), - "null" => jsonDocument.Deserialize(options), - "undefined" => jsonDocument.Deserialize(options), - "symbol" => jsonDocument.Deserialize(options), - "object" => jsonDocument.Deserialize(options), - "function" => jsonDocument.Deserialize(options), - "regexp" => jsonDocument.Deserialize(options), - "date" => jsonDocument.Deserialize(options), - "map" => jsonDocument.Deserialize(options), - "set" => jsonDocument.Deserialize(options), - "weakmap" => jsonDocument.Deserialize(options), - "weakset" => jsonDocument.Deserialize(options), - "generator" => jsonDocument.Deserialize(options), - "error" => jsonDocument.Deserialize(options), - "proxy" => jsonDocument.Deserialize(options), - "promise" => jsonDocument.Deserialize(options), - "typedarray" => jsonDocument.Deserialize(options), - "arraybuffer" => jsonDocument.Deserialize(options), - "nodelist" => jsonDocument.Deserialize(options), - "htmlcollection" => jsonDocument.Deserialize(options), - "node" => jsonDocument.Deserialize(options), - "window" => jsonDocument.Deserialize(options), + "number" => jsonDocument.Deserialize(options), + "boolean" => jsonDocument.Deserialize(options), + "string" => jsonDocument.Deserialize(options), + "null" => jsonDocument.Deserialize(options), + "undefined" => jsonDocument.Deserialize(options), + "symbol" => jsonDocument.Deserialize(options), + "array" => jsonDocument.Deserialize(options), + "object" => jsonDocument.Deserialize(options), + "function" => jsonDocument.Deserialize(options), + "regexp" => jsonDocument.Deserialize(options), + "date" => jsonDocument.Deserialize(options), + "map" => jsonDocument.Deserialize(options), + "set" => jsonDocument.Deserialize(options), + "weakmap" => jsonDocument.Deserialize(options), + "weakset" => jsonDocument.Deserialize(options), + "generator" => jsonDocument.Deserialize(options), + "error" => jsonDocument.Deserialize(options), + "proxy" => jsonDocument.Deserialize(options), + "promise" => jsonDocument.Deserialize(options), + "typedarray" => jsonDocument.Deserialize(options), + "arraybuffer" => jsonDocument.Deserialize(options), + "nodelist" => jsonDocument.Deserialize(options), + "htmlcollection" => jsonDocument.Deserialize(options), + "node" => jsonDocument.Deserialize(options), + "window" => jsonDocument.Deserialize(options), _ => null, }; } diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/PreloadScriptConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/PreloadScriptConverter.cs index 53dba4b424388..4b68ce22c52ff 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/PreloadScriptConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/PreloadScriptConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class PreloadScriptConverter : JsonConverter diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/PrintPageRangeConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/PrintPageRangeConverter.cs index afd375d078a59..76ffd9b34ad4e 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/PrintPageRangeConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/PrintPageRangeConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class PrintPageRangeConverter : JsonConverter diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RealmConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RealmConverter.cs index ab2baedc6fd28..60cde6d1152db 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RealmConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RealmConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class RealmConverter : JsonConverter diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RealmTypeConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RealmTypeConverter.cs index f92966b1a3f1f..048250fd526d5 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RealmTypeConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RealmTypeConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class RealmTypeConverter : JsonConverter @@ -27,6 +29,19 @@ public override RealmType Read(ref Utf8JsonReader reader, Type typeToConvert, Js public override void Write(Utf8JsonWriter writer, RealmType value, JsonSerializerOptions options) { - throw new NotImplementedException(); + var str = value switch + { + RealmType.Window => "window", + RealmType.DedicatedWorker => "dedicated-worker", + RealmType.SharedWorker => "shared-worker", + RealmType.ServiceWorker => "service-worker", + RealmType.Worker => "worker", + RealmType.PaintWorker => "paint-worker", + RealmType.AudioWorker => "audio-worker", + RealmType.Worklet => "worklet", + _ => throw new JsonException($"Unrecognized '{value}' value of {typeof(RealmType)}."), + }; + + writer.WriteStringValue(str); } } diff --git a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RequestConverter.cs b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RequestConverter.cs index 7a15a569faa06..0524246d6b3b3 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RequestConverter.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Json/Converters/RequestConverter.cs @@ -3,6 +3,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Json.Converters; internal class RequestConverter : JsonConverter diff --git a/dotnet/src/webdriver/BiDi/Communication/Message.cs b/dotnet/src/webdriver/BiDi/Communication/Message.cs index 75925ed4d7592..c00a2777a33f4 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Message.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Message.cs @@ -1,5 +1,7 @@ using System.Text.Json; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication; // https://github.com/dotnet/runtime/issues/72604 diff --git a/dotnet/src/webdriver/BiDi/Communication/Transport/ITransport.cs b/dotnet/src/webdriver/BiDi/Communication/Transport/ITransport.cs index 404d62734f9d2..3ed6d2e32a4cf 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Transport/ITransport.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Transport/ITransport.cs @@ -3,9 +3,11 @@ using System.Threading; using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Transport; -public interface ITransport : IDisposable +interface ITransport : IDisposable { Task ConnectAsync(CancellationToken cancellationToken); diff --git a/dotnet/src/webdriver/BiDi/Communication/Transport/WebSocketTransport.cs b/dotnet/src/webdriver/BiDi/Communication/Transport/WebSocketTransport.cs index 52cd5c0ffa7f2..76384f51b1261 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Transport/WebSocketTransport.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Transport/WebSocketTransport.cs @@ -7,9 +7,11 @@ using System.Text; using OpenQA.Selenium.Internal.Logging; +#nullable enable + namespace OpenQA.Selenium.BiDi.Communication.Transport; -public class WebSocketTransport(Uri _uri) : ITransport, IDisposable +class WebSocketTransport(Uri _uri) : ITransport, IDisposable { private readonly static ILogger _logger = Log.GetLogger(); @@ -59,7 +61,7 @@ public async Task SendAsJsonAsync(Command command, JsonSerializerOptions jsonSer { if (_logger.IsEnabled(LogEventLevel.Trace)) { - _logger.Trace($"BiDi SND >> {buffer.Length} > {Encoding.UTF8.GetString(buffer)}"); + _logger.Trace($"BiDi SND >> {Encoding.UTF8.GetString(buffer)}"); } await _webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, cancellationToken).ConfigureAwait(false); diff --git a/dotnet/src/webdriver/BiDi/EventArgs.cs b/dotnet/src/webdriver/BiDi/EventArgs.cs index 01d5cd3280acf..30361ca95e6cb 100644 --- a/dotnet/src/webdriver/BiDi/EventArgs.cs +++ b/dotnet/src/webdriver/BiDi/EventArgs.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Modules.BrowsingContext; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi; public abstract record EventArgs(BiDi BiDi) diff --git a/dotnet/src/webdriver/BiDi/Modules/Browser/BrowserModule.cs b/dotnet/src/webdriver/BiDi/Modules/Browser/BrowserModule.cs index e42982b773d8c..ba7fb0496dd6d 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Browser/BrowserModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Browser/BrowserModule.cs @@ -2,6 +2,8 @@ using System.Threading.Tasks; using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Browser; public sealed class BrowserModule(Broker broker) : Module(broker) @@ -16,11 +18,9 @@ public async Task CreateUserContextAsync(CreateUserContextOptio return await Broker.ExecuteCommandAsync(new CreateUserContextCommand(), options).ConfigureAwait(false); } - public async Task> GetUserContextsAsync(GetUserContextsOptions? options = null) + public async Task GetUserContextsAsync(GetUserContextsOptions? options = null) { - var result = await Broker.ExecuteCommandAsync(new GetUserContextsCommand(), options).ConfigureAwait(false); - - return result.UserContexts; + return await Broker.ExecuteCommandAsync(new GetUserContextsCommand(), options).ConfigureAwait(false); } public async Task RemoveUserContextAsync(UserContext userContext, RemoveUserContextOptions? options = null) diff --git a/dotnet/src/webdriver/BiDi/Modules/Browser/CloseCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Browser/CloseCommand.cs index 345a3c5a22935..59f9db7374929 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Browser/CloseCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Browser/CloseCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Browser; internal class CloseCommand() : Command(CommandParameters.Empty); diff --git a/dotnet/src/webdriver/BiDi/Modules/Browser/CreateUserContextCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Browser/CreateUserContextCommand.cs index 9b84d1ee11532..ce44e5ae2f367 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Browser/CreateUserContextCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Browser/CreateUserContextCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Browser; internal class CreateUserContextCommand() : Command(CommandParameters.Empty); diff --git a/dotnet/src/webdriver/BiDi/Modules/Browser/GetUserContextsCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Browser/GetUserContextsCommand.cs index 5a1a0a211cf1c..161a1bf286f1e 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Browser/GetUserContextsCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Browser/GetUserContextsCommand.cs @@ -1,10 +1,29 @@ using OpenQA.Selenium.BiDi.Communication; +using System.Collections; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Browser; internal class GetUserContextsCommand() : Command(CommandParameters.Empty); public record GetUserContextsOptions : CommandOptions; -public record GetUserContextsResult(IReadOnlyList UserContexts); +public record GetUserContextsResult : IReadOnlyList +{ + private readonly IReadOnlyList _userContexts; + + internal GetUserContextsResult(IReadOnlyList userContexts) + { + _userContexts = userContexts; + } + + public UserContextInfo this[int index] => _userContexts[index]; + + public int Count => _userContexts.Count; + + public IEnumerator GetEnumerator() => _userContexts.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => (_userContexts as IEnumerable).GetEnumerator(); +} diff --git a/dotnet/src/webdriver/BiDi/Modules/Browser/RemoveUserContextCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Browser/RemoveUserContextCommand.cs index b937ad48ffc80..7877eb0f710ff 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Browser/RemoveUserContextCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Browser/RemoveUserContextCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Browser; internal class RemoveUserContextCommand(RemoveUserContextCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Browser/UserContext.cs b/dotnet/src/webdriver/BiDi/Modules/Browser/UserContext.cs index 5e8bcd712062a..a2e689d494aaa 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Browser/UserContext.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Browser/UserContext.cs @@ -1,6 +1,8 @@ using System; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Browser; public class UserContext : IAsyncDisposable @@ -13,7 +15,7 @@ internal UserContext(BiDi bidi, string id) Id = id; } - public string Id { get; } + internal string Id { get; } public Task RemoveAsync() { @@ -24,4 +26,16 @@ public async ValueTask DisposeAsync() { await RemoveAsync().ConfigureAwait(false); } + + public override bool Equals(object? obj) + { + if (obj is UserContext userContextObj) return userContextObj.Id == Id; + + return false; + } + + public override int GetHashCode() + { + return Id.GetHashCode(); + } } diff --git a/dotnet/src/webdriver/BiDi/Modules/Browser/UserContextInfo.cs b/dotnet/src/webdriver/BiDi/Modules/Browser/UserContextInfo.cs index 7f4949b49a2fa..99b7e59a486b2 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Browser/UserContextInfo.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Browser/UserContextInfo.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Browser; public record UserContextInfo(UserContext UserContext); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/ActivateCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/ActivateCommand.cs index f05354060e89a..4934e51b815bd 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/ActivateCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/ActivateCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; internal class ActivateCommand(ActivateCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContext.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContext.cs index 80c7b7d49fb70..e83f671ae227e 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContext.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContext.cs @@ -2,6 +2,8 @@ using System.Threading.Tasks; using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; public class BrowsingContext @@ -13,7 +15,7 @@ internal BrowsingContext(BiDi bidi, string id) _logModule = new Lazy(() => new BrowsingContextLogModule(this, BiDi.Log)); _networkModule = new Lazy(() => new BrowsingContextNetworkModule(this, BiDi.Network)); - _scriptModule = new Lazy(() => new BrowsingContextScriptModule(this, BiDi.ScriptModule)); + _scriptModule = new Lazy(() => new BrowsingContextScriptModule(this, BiDi.Script)); _storageModule = new Lazy(() => new BrowsingContextStorageModule(this, BiDi.Storage)); _inputModule = new Lazy(() => new BrowsingContextInputModule(this, BiDi.InputModule)); } @@ -40,37 +42,37 @@ internal BrowsingContext(BiDi bidi, string id) public Task NavigateAsync(string url, NavigateOptions? options = null) { - return BiDi.BrowsingContextModule.NavigateAsync(this, url, options); + return BiDi.BrowsingContext.NavigateAsync(this, url, options); } public Task ReloadAsync(ReloadOptions? options = null) { - return BiDi.BrowsingContextModule.ReloadAsync(this, options); + return BiDi.BrowsingContext.ReloadAsync(this, options); } public Task ActivateAsync(ActivateOptions? options = null) { - return BiDi.BrowsingContextModule.ActivateAsync(this, options); + return BiDi.BrowsingContext.ActivateAsync(this, options); } - public Task> LocateNodesAsync(Locator locator, LocateNodesOptions? options = null) + public Task LocateNodesAsync(Locator locator, LocateNodesOptions? options = null) { - return BiDi.BrowsingContextModule.LocateNodesAsync(this, locator, options); + return BiDi.BrowsingContext.LocateNodesAsync(this, locator, options); } public Task CaptureScreenshotAsync(CaptureScreenshotOptions? options = null) { - return BiDi.BrowsingContextModule.CaptureScreenshotAsync(this, options); + return BiDi.BrowsingContext.CaptureScreenshotAsync(this, options); } public Task CloseAsync(CloseOptions? options = null) { - return BiDi.BrowsingContextModule.CloseAsync(this, options); + return BiDi.BrowsingContext.CloseAsync(this, options); } public Task TraverseHistoryAsync(int delta, TraverseHistoryOptions? options = null) { - return BiDi.BrowsingContextModule.TraverseHistoryAsync(this, delta, options); + return BiDi.BrowsingContext.TraverseHistoryAsync(this, delta, options); } public Task NavigateBackAsync(TraverseHistoryOptions? options = null) @@ -85,17 +87,17 @@ public Task NavigateForwardAsync(TraverseHistoryOptions? options = null) public Task SetViewportAsync(SetViewportOptions? options = null) { - return BiDi.BrowsingContextModule.SetViewportAsync(this, options); + return BiDi.BrowsingContext.SetViewportAsync(this, options); } public Task PrintAsync(PrintOptions? options = null) { - return BiDi.BrowsingContextModule.PrintAsync(this, options); + return BiDi.BrowsingContext.PrintAsync(this, options); } public Task HandleUserPromptAsync(HandleUserPromptOptions? options = null) { - return BiDi.BrowsingContextModule.HandleUserPromptAsync(this, options); + return BiDi.BrowsingContext.HandleUserPromptAsync(this, options); } public Task> GetTreeAsync(BrowsingContextGetTreeOptions? options = null) @@ -105,77 +107,77 @@ public Task> GetTreeAsync(BrowsingContextGetT Root = this }; - return BiDi.BrowsingContextModule.GetTreeAsync(getTreeOptions); + return BiDi.BrowsingContext.GetTreeAsync(getTreeOptions); } public Task OnNavigationStartedAsync(Func handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnNavigationStartedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnNavigationStartedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnNavigationStartedAsync(Action handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnNavigationStartedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnNavigationStartedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnFragmentNavigatedAsync(Func handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnFragmentNavigatedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnFragmentNavigatedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnFragmentNavigatedAsync(Action handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnFragmentNavigatedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnFragmentNavigatedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnDomContentLoadedAsync(Func handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnDomContentLoadedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnDomContentLoadedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnLoadAsync(Action handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnLoadAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnLoadAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnLoadAsync(Func handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnLoadAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnLoadAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnDownloadWillBeginAsync(Action handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnDownloadWillBeginAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnDownloadWillBeginAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnDownloadWillBeginAsync(Func handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnDownloadWillBeginAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnDownloadWillBeginAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnNavigationAbortedAsync(Action handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnNavigationAbortedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnNavigationAbortedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnNavigationAbortedAsync(Func handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnNavigationAbortedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnNavigationAbortedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnNavigationFailedAsync(Action handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnNavigationFailedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnNavigationFailedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnNavigationFailedAsync(Func handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnNavigationFailedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnNavigationFailedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public Task OnDomContentLoadedAsync(Action handler, SubscriptionOptions? options = null) { - return BiDi.BrowsingContextModule.OnDomContentLoadedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); + return BiDi.BrowsingContext.OnDomContentLoadedAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [this] }); } public override bool Equals(object? obj) diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextInfo.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextInfo.cs index 52f2edeb972e0..f2303915feb90 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextInfo.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextInfo.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; // TODO: Split it to separate class with just info and event args diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextInputModule.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextInputModule.cs index ceae616cb2378..919758b14ba79 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextInputModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextInputModule.cs @@ -2,16 +2,19 @@ using OpenQA.Selenium.BiDi.Modules.Input; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; public class BrowsingContextInputModule(BrowsingContext context, InputModule inputModule) { public Task PerformActionsAsync(IEnumerable actions, PerformActionsOptions? options = null) { - options ??= new(); - - options.Actions = actions; + return inputModule.PerformActionsAsync(context, actions, options); + } - return inputModule.PerformActionsAsync(context, options); + public Task ReleaseActionsAsync(ReleaseActionsOptions? options = null) + { + return inputModule.ReleaseActionsAsync(context, options); } } diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextLogModule.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextLogModule.cs index 03187efd7265c..a085bb07de630 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextLogModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextLogModule.cs @@ -2,11 +2,13 @@ using System.Threading.Tasks; using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; public class BrowsingContextLogModule(BrowsingContext context, LogModule logModule) { - public Task OnEntryAddedAsync(Func handler, SubscriptionOptions options = null) + public Task OnEntryAddedAsync(Func handler, SubscriptionOptions? options = null) { return logModule.OnEntryAddedAsync(async args => { @@ -17,7 +19,7 @@ public Task OnEntryAddedAsync(Func handler, Su }, options); } - public Task OnEntryAddedAsync(Action handler, SubscriptionOptions options = null) + public Task OnEntryAddedAsync(Action handler, SubscriptionOptions? options = null) { return logModule.OnEntryAddedAsync(args => { diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextModule.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextModule.cs index fe7a1d3f36343..690df63f7ab2d 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextModule.cs @@ -3,6 +3,8 @@ using System.Threading.Tasks; using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; public class BrowsingContextModule(Broker broker) : Module(broker) @@ -42,7 +44,7 @@ public async Task ActivateAsync(BrowsingContext context, ActivateOptions? option await Broker.ExecuteCommandAsync(new ActivateCommand(@params), options).ConfigureAwait(false); } - public async Task> LocateNodesAsync(BrowsingContext context, Locator locator, LocateNodesOptions? options = null) + public async Task LocateNodesAsync(BrowsingContext context, Locator locator, LocateNodesOptions? options = null) { var @params = new LocateNodesCommandParameters(context, locator); @@ -53,9 +55,7 @@ public async Task ActivateAsync(BrowsingContext context, ActivateOptions? option @params.StartNodes = options.StartNodes; } - var result = await Broker.ExecuteCommandAsync(new LocateNodesCommand(@params), options).ConfigureAwait(false); - - return result.Nodes; + return await Broker.ExecuteCommandAsync(new LocateNodesCommand(@params), options).ConfigureAwait(false); } public async Task CaptureScreenshotAsync(BrowsingContext context, CaptureScreenshotOptions? options = null) diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextNetworkModule.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextNetworkModule.cs index 4b4d805eb3041..e25f255f423c7 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextNetworkModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextNetworkModule.cs @@ -2,6 +2,8 @@ using System; using OpenQA.Selenium.BiDi.Modules.Network; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; public class BrowsingContextNetworkModule(BrowsingContext context, NetworkModule networkModule) @@ -34,7 +36,7 @@ public async Task InterceptResponseAsync(Func InterceptAuthenticationAsync(Func handler, BrowsingContextAddInterceptOptions? interceptOptions = null, SubscriptionOptions? options = null) + public async Task InterceptAuthAsync(Func handler, BrowsingContextAddInterceptOptions? interceptOptions = null, SubscriptionOptions? options = null) { AddInterceptOptions addInterceptOptions = new(interceptOptions) { @@ -88,7 +90,12 @@ public Task OnFetchErrorAsync(Action handler, return networkModule.OnFetchErrorAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [context] }); } - internal Task OnAuthRequiredAsync(Func handler, SubscriptionOptions? options = null) + public Task OnAuthRequiredAsync(Func handler, SubscriptionOptions? options = null) + { + return networkModule.OnAuthRequiredAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [context] }); + } + + public Task OnAuthRequiredAsync(Action handler, SubscriptionOptions? options = null) { return networkModule.OnAuthRequiredAsync(handler, new BrowsingContextsSubscriptionOptions(options) { Contexts = [context] }); } diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextScriptModule.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextScriptModule.cs index d2756ecc503cc..f6f8b77e43c55 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextScriptModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextScriptModule.cs @@ -2,6 +2,8 @@ using OpenQA.Selenium.BiDi.Modules.Script; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; public class BrowsingContextScriptModule(BrowsingContext context, ScriptModule scriptModule) @@ -25,9 +27,9 @@ public async Task> GetRealmsAsync(GetRealmsOptions? opt return await scriptModule.GetRealmsAsync(options).ConfigureAwait(false); } - public Task EvaluateAsync(string expression, bool awaitPromise, EvaluateOptions? options = null, ContextTargetOptions? targetOptions = null) + public Task EvaluateAsync(string expression, bool awaitPromise, EvaluateOptions? options = null, ContextTargetOptions? targetOptions = null) { - var contextTarget = new ContextTarget(context); + var contextTarget = new Target.Context(context); if (targetOptions is not null) { @@ -37,16 +39,16 @@ public Task EvaluateAsync(string expression, bool awaitPromise, Eva return scriptModule.EvaluateAsync(expression, awaitPromise, contextTarget, options); } - public async Task EvaluateAsync(string expression, bool awaitPromise, EvaluateOptions? options = null) + public async Task EvaluateAsync(string expression, bool awaitPromise, EvaluateOptions? options = null, ContextTargetOptions? targetOptions = null) { - var remoteValue = await EvaluateAsync(expression, awaitPromise, options).ConfigureAwait(false); + var result = await EvaluateAsync(expression, awaitPromise, options, targetOptions).ConfigureAwait(false); - return remoteValue.ConvertTo(); + return result.Result.ConvertTo(); } - public Task CallFunctionAsync(string functionDeclaration, bool awaitPromise, CallFunctionOptions? options = null, ContextTargetOptions? targetOptions = null) + public Task CallFunctionAsync(string functionDeclaration, bool awaitPromise, CallFunctionOptions? options = null, ContextTargetOptions? targetOptions = null) { - var contextTarget = new ContextTarget(context); + var contextTarget = new Target.Context(context); if (targetOptions is not null) { @@ -55,4 +57,11 @@ public Task CallFunctionAsync(string functionDeclaration, bool awai return scriptModule.CallFunctionAsync(functionDeclaration, awaitPromise, contextTarget, options); } + + public async Task CallFunctionAsync(string functionDeclaration, bool awaitPromise, CallFunctionOptions? options = null, ContextTargetOptions? targetOptions = null) + { + var result = await CallFunctionAsync(functionDeclaration, awaitPromise, options, targetOptions).ConfigureAwait(false); + + return result.Result.ConvertTo(); + } } diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextStorageModule.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextStorageModule.cs index 810f1ab0b32db..d89343d1ab0f9 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextStorageModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/BrowsingContextStorageModule.cs @@ -1,6 +1,8 @@ using System.Threading.Tasks; using OpenQA.Selenium.BiDi.Modules.Storage; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; public class BrowsingContextStorageModule(BrowsingContext context, StorageModule storageModule) @@ -9,7 +11,7 @@ public Task GetCookiesAsync(GetCookiesOptions? options = null) { options ??= new(); - options.Partition = new BrowsingContextPartitionDescriptor(context); + options.Partition = new PartitionDescriptor.Context(context); return storageModule.GetCookiesAsync(options); } @@ -18,7 +20,7 @@ public async Task DeleteCookiesAsync(DeleteCookiesOptions? options { options ??= new(); - options.Partition = new BrowsingContextPartitionDescriptor(context); + options.Partition = new PartitionDescriptor.Context(context); var res = await storageModule.DeleteCookiesAsync(options).ConfigureAwait(false); @@ -29,7 +31,7 @@ public async Task SetCookieAsync(PartialCookie cookie, SetCookieOp { options ??= new(); - options.Partition = new BrowsingContextPartitionDescriptor(context); + options.Partition = new PartitionDescriptor.Context(context); var res = await storageModule.SetCookieAsync(cookie, options).ConfigureAwait(false); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CaptureScreenshotCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CaptureScreenshotCommand.cs index 69ea01b0e70d8..df5f203c1d3fc 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CaptureScreenshotCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CaptureScreenshotCommand.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Communication; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; internal class CaptureScreenshotCommand(CaptureScreenshotCommandParameters @params) : Command(@params); @@ -35,13 +37,14 @@ public record struct ImageFormat(string Type) } [JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -[JsonDerivedType(typeof(BoxClipRectangle), "box")] -[JsonDerivedType(typeof(ElementClipRectangle), "element")] -public abstract record ClipRectangle; - -public record BoxClipRectangle(double X, double Y, double Width, double Height) : ClipRectangle; +[JsonDerivedType(typeof(Box), "box")] +[JsonDerivedType(typeof(Element), "element")] +public abstract record ClipRectangle +{ + public record Box(double X, double Y, double Width, double Height) : ClipRectangle; -public record ElementClipRectangle(Script.SharedReference Element) : ClipRectangle; + public record Element([property: JsonPropertyName("element")] Script.SharedReference SharedReference) : ClipRectangle; +} public record CaptureScreenshotResult(string Data) { diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CloseCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CloseCommand.cs index ac531a28699ac..e3f3279a97955 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CloseCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CloseCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; internal class CloseCommand(CloseCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CreateCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CreateCommand.cs index 86a81bcada46a..a70f89726c364 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CreateCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/CreateCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; internal class CreateCommand(CreateCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/GetTreeCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/GetTreeCommand.cs index fe6a38c1fe0bc..eb03c29856266 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/GetTreeCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/GetTreeCommand.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Communication; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; internal class GetTreeCommand(GetTreeCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/HandleUserPromptCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/HandleUserPromptCommand.cs index 2dd2888d19a87..a566ef6edf963 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/HandleUserPromptCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/HandleUserPromptCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; class HandleUserPromptCommand(HandleUserPromptCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/LocateNodesCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/LocateNodesCommand.cs index 2dce843e557a3..d32188ae7cce3 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/LocateNodesCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/LocateNodesCommand.cs @@ -1,6 +1,9 @@ using OpenQA.Selenium.BiDi.Communication; +using System.Collections; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; internal class LocateNodesCommand(LocateNodesCommandParameters @params) : Command(@params); @@ -23,4 +26,20 @@ public record LocateNodesOptions : CommandOptions public IEnumerable? StartNodes { get; set; } } -public record LocateNodesResult(IReadOnlyList Nodes); +public record LocateNodesResult : IReadOnlyList +{ + private readonly IReadOnlyList _nodes; + + internal LocateNodesResult(IReadOnlyList nodes) + { + _nodes = nodes; + } + + public Script.RemoteValue.Node this[int index] => _nodes[index]; + + public int Count => _nodes.Count; + + public IEnumerator GetEnumerator() => _nodes.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => (_nodes as IEnumerable).GetEnumerator(); +} diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/Locator.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/Locator.cs index d931019afd446..d22215f5f6d92 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/Locator.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/Locator.cs @@ -1,43 +1,37 @@ using System.Text.Json.Serialization; -using static OpenQA.Selenium.BiDi.Modules.BrowsingContext.AccessibilityLocator; + +#nullable enable namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; [JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -[JsonDerivedType(typeof(AccessibilityLocator), "accessibility")] -[JsonDerivedType(typeof(CssLocator), "css")] -[JsonDerivedType(typeof(InnerTextLocator), "innerText")] -[JsonDerivedType(typeof(XPathLocator), "xpath")] +[JsonDerivedType(typeof(Accessibility), "accessibility")] +[JsonDerivedType(typeof(Css), "css")] +[JsonDerivedType(typeof(InnerText), "innerText")] +[JsonDerivedType(typeof(XPath), "xpath")] public abstract record Locator { - public static CssLocator Css(string value) - => new(value); - - public static InnerTextLocator InnerText(string value, bool? ignoreCase = null, MatchType? matchType = null, long? maxDepth = null) - => new(value) { IgnoreCase = ignoreCase, MatchType = matchType, MaxDepth = maxDepth }; - - public static XPathLocator XPath(string value) - => new(value); -} - -public record AccessibilityLocator(AccessibilityValue Value) : Locator -{ - public record AccessibilityValue + public record Accessibility(Accessibility.AccessibilityValue Value) : Locator { - public string? Name { get; set; } - public string? Role { get; set; } + public record AccessibilityValue + { + public string? Name { get; set; } + public string? Role { get; set; } + } } -} -public record CssLocator(string Value) : Locator; + public record Css(string Value) : Locator; -public record InnerTextLocator(string Value) : Locator -{ - public bool? IgnoreCase { get; set; } + public record InnerText(string Value) : Locator + { + public bool? IgnoreCase { get; set; } - public MatchType? MatchType { get; set; } + public MatchType? MatchType { get; set; } - public long? MaxDepth { get; set; } + public long? MaxDepth { get; set; } + } + + public record XPath(string Value) : Locator; } public enum MatchType @@ -45,5 +39,3 @@ public enum MatchType Full, Partial } - -public record XPathLocator(string Value) : Locator; diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/NavigateCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/NavigateCommand.cs index 401c0ec2093d4..7481a6141e430 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/NavigateCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/NavigateCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; internal class NavigateCommand(NavigateCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/Navigation.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/Navigation.cs index 0b56229ade1de..3ce169d0f40c6 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/Navigation.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/Navigation.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; public record Navigation(string Id); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/NavigationInfo.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/NavigationInfo.cs index fe7a83399bf11..e934e3030d305 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/NavigationInfo.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/NavigationInfo.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; public record NavigationInfo(BiDi BiDi, BrowsingContext Context, Navigation Navigation, DateTimeOffset Timestamp, string Url) diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/PrintCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/PrintCommand.cs index 66c270c12dd97..70fa6bcdd1f2a 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/PrintCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/PrintCommand.cs @@ -2,6 +2,8 @@ using System; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; internal class PrintCommand(PrintCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/ReloadCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/ReloadCommand.cs index fea265ce15200..1dd805fd0a00b 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/ReloadCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/ReloadCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; internal class ReloadCommand(ReloadCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/SetViewportCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/SetViewportCommand.cs index 0ed6e869e85a7..a43cb30157e15 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/SetViewportCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/SetViewportCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; internal class SetViewportCommand(SetViewportCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/TraverseHistoryCommand.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/TraverseHistoryCommand.cs index 65797e0164cd0..b8c78b2601adb 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/TraverseHistoryCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/TraverseHistoryCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; internal class TraverseHistoryCommand(TraverseHistoryCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/UserPromptClosedEventArgs.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/UserPromptClosedEventArgs.cs index fa59f7262fcc2..c7123e89f76be 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/UserPromptClosedEventArgs.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/UserPromptClosedEventArgs.cs @@ -1,5 +1,7 @@ using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; public record UserPromptClosedEventArgs(BiDi BiDi, BrowsingContext Context, bool Accepted) diff --git a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/UserPromptOpenedEventArgs.cs b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/UserPromptOpenedEventArgs.cs index 8e6658bd79523..03dd37be8cbeb 100644 --- a/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/UserPromptOpenedEventArgs.cs +++ b/dotnet/src/webdriver/BiDi/Modules/BrowsingContext/UserPromptOpenedEventArgs.cs @@ -1,5 +1,7 @@ using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.BrowsingContext; public record UserPromptOpenedEventArgs(BiDi BiDi, BrowsingContext Context, UserPromptType Type, string Message) diff --git a/dotnet/src/webdriver/BiDi/Modules/Input/InputModule.cs b/dotnet/src/webdriver/BiDi/Modules/Input/InputModule.cs index a31b0371dfb4a..53f913132c758 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Input/InputModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Input/InputModule.cs @@ -1,18 +1,16 @@ using OpenQA.Selenium.BiDi.Communication; +using System.Collections.Generic; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Input; public sealed class InputModule(Broker broker) : Module(broker) { - public async Task PerformActionsAsync(BrowsingContext.BrowsingContext context, PerformActionsOptions? options = null) + public async Task PerformActionsAsync(BrowsingContext.BrowsingContext context, IEnumerable actions, PerformActionsOptions? options = null) { - var @params = new PerformActionsCommandParameters(context); - - if (options is not null) - { - @params.Actions = options.Actions; - } + var @params = new PerformActionsCommandParameters(context, actions); await Broker.ExecuteCommandAsync(new PerformActionsCommand(@params), options).ConfigureAwait(false); } @@ -21,6 +19,6 @@ public async Task ReleaseActionsAsync(BrowsingContext.BrowsingContext context, R { var @params = new ReleaseActionsCommandParameters(context); - await Broker.ExecuteCommandAsync(new ReleaseActionsCommand(@params), options); + await Broker.ExecuteCommandAsync(new ReleaseActionsCommand(@params), options).ConfigureAwait(false); } } diff --git a/dotnet/src/webdriver/BiDi/Modules/Input/Key.cs b/dotnet/src/webdriver/BiDi/Modules/Input/Key.cs new file mode 100644 index 0000000000000..a7d99ffca3ac9 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Modules/Input/Key.cs @@ -0,0 +1,8 @@ +//namespace OpenQA.Selenium.BiDi.Modules.Input; + +//partial record Key +//{ +// public const char Shift = '\uE008'; + +// public const char Pause = '\uE00B'; +//} diff --git a/dotnet/src/webdriver/BiDi/Modules/Input/Origin.cs b/dotnet/src/webdriver/BiDi/Modules/Input/Origin.cs new file mode 100644 index 0000000000000..5cc909cd6aabe --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Modules/Input/Origin.cs @@ -0,0 +1,17 @@ +using System.Text.Json.Serialization; + +#nullable enable + +namespace OpenQA.Selenium.BiDi.Modules.Input; + +public abstract record Origin +{ + public record Viewport() : Origin; + + public record Pointer() : Origin; + + public record Element([property: JsonPropertyName("element")] Script.SharedReference SharedReference) : Origin + { + public string Type { get; } = "element"; + } +} diff --git a/dotnet/src/webdriver/BiDi/Modules/Input/PerformActionsCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Input/PerformActionsCommand.cs index 0a719c7513ce7..878b576417fba 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Input/PerformActionsCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Input/PerformActionsCommand.cs @@ -1,74 +1,12 @@ using OpenQA.Selenium.BiDi.Communication; -using System; using System.Collections.Generic; -using System.Text.Json.Serialization; + +#nullable enable namespace OpenQA.Selenium.BiDi.Modules.Input; internal class PerformActionsCommand(PerformActionsCommandParameters @params) : Command(@params); -internal record PerformActionsCommandParameters(BrowsingContext.BrowsingContext Context) : CommandParameters -{ - public IEnumerable? Actions { get; set; } -} - -public record PerformActionsOptions : CommandOptions -{ - public IEnumerable? Actions { get; set; } = []; -} - -[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -[JsonDerivedType(typeof(KeySourceActions), "key")] -public abstract record SourceActions -{ - public static KeySourceActions Press(string text) - { - var keySourceActions = new KeySourceActions(); - - foreach (var character in text) - { - keySourceActions.Actions.AddRange([ - new KeyDownAction(character.ToString()), - new KeyUpAction(character.ToString()) - ]); - } - - return keySourceActions; - } -} - -public record KeySourceActions : SourceActions -{ - public string Id { get; set; } = Guid.NewGuid().ToString(); - - public List Actions { get; set; } = []; - - public new KeySourceActions Press(string text) - { - Actions.AddRange(SourceActions.Press(text).Actions); - - return this; - } - - public KeySourceActions Pause(long? duration = null) - { - Actions.Add(new KeyPauseAction { Duration = duration }); - - return this; - } -} - -[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -[JsonDerivedType(typeof(KeyPauseAction), "pause")] -[JsonDerivedType(typeof(KeyDownAction), "keyDown")] -[JsonDerivedType(typeof(KeyUpAction), "keyUp")] -public abstract record KeySourceAction; - -public record KeyPauseAction : KeySourceAction -{ - public long? Duration { get; set; } -} - -public record KeyDownAction(string Value) : KeySourceAction; +internal record PerformActionsCommandParameters(BrowsingContext.BrowsingContext Context, IEnumerable Actions) : CommandParameters; -public record KeyUpAction(string Value) : KeySourceAction; +public record PerformActionsOptions : CommandOptions; diff --git a/dotnet/src/webdriver/BiDi/Modules/Input/ReleaseActionsCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Input/ReleaseActionsCommand.cs index 0ee6d6345d21b..2331e1bec6c49 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Input/ReleaseActionsCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Input/ReleaseActionsCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Input; internal class ReleaseActionsCommand(ReleaseActionsCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Input/SequentialSourceActions.cs b/dotnet/src/webdriver/BiDi/Modules/Input/SequentialSourceActions.cs new file mode 100644 index 0000000000000..dfdbc3f69a331 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Modules/Input/SequentialSourceActions.cs @@ -0,0 +1,161 @@ +//using System.Collections; +//using System.Collections.Generic; +//using System.Linq; + +//namespace OpenQA.Selenium.BiDi.Modules.Input; + +//public interface ISequentialSourceActions : IEnumerable +//{ +// ISequentialSourceActions Pause(int duration); + +// ISequentialSourceActions Type(string text); +// ISequentialSourceActions KeyDown(char key); +// ISequentialSourceActions KeyUp(char key); + +// ISequentialSourceActions PointerDown(int button, PointerDownOptions? options = null); +// ISequentialSourceActions PointerUp(int button); +// ISequentialSourceActions PointerMove(int x, int y, PointerMoveOptions? options = null); +//} + +//public record SequentialSourceActions : ISequentialSourceActions +//{ +// private readonly KeyActions _keyActions = []; +// private readonly PointerActions _pointerActions = []; +// private readonly WheelActions _wheelActions = []; +// private readonly WheelActions _noneActions = []; + +// public ISequentialSourceActions Pause(int duration) +// { +// _noneActions.Add(new Pause { Duration = duration }); + +// return Normalized(); +// } + +// public ISequentialSourceActions Type(string text) +// { +// _keyActions.Type(text); + +// return Normalized(); +// } + +// public ISequentialSourceActions KeyDown(char key) +// { +// _keyActions.Add(new Key.Down(key)); + +// return Normalized(); +// } + +// public ISequentialSourceActions KeyUp(char key) +// { +// _keyActions.Add(new Key.Up(key)); + +// return Normalized(); +// } + +// public ISequentialSourceActions PointerDown(int button, PointerDownOptions? options = null) +// { +// _pointerActions.Add(new Pointer.Down(button) +// { +// Width = options?.Width, +// Height = options?.Height, +// Pressure = options?.Pressure, +// TangentialPressure = options?.TangentialPressure, +// Twist = options?.Twist, +// AltitudeAngle = options?.AltitudeAngle, +// AzimuthAngle = options?.AzimuthAngle +// }); + +// return Normalized(); +// } + +// public ISequentialSourceActions PointerUp(int button) +// { +// _pointerActions.Add(new Pointer.Up(button)); + +// return Normalized(); +// } + +// public ISequentialSourceActions PointerMove(int x, int y, PointerMoveOptions? options = null) +// { +// _pointerActions.Add(new Pointer.Move(x, y) +// { +// Duration = options?.Duration, +// Origin = options?.Origin, +// Width = options?.Width, +// Height = options?.Height, +// Pressure = options?.Pressure, +// TangentialPressure = options?.TangentialPressure, +// Twist = options?.Twist, +// AltitudeAngle = options?.AltitudeAngle, +// AzimuthAngle = options?.AzimuthAngle +// }); + +// return Normalized(); +// } + +// private SequentialSourceActions Normalized() +// { +// var max = new[] { _keyActions.Count(), _pointerActions.Count(), _wheelActions.Count(), _noneActions.Count() }.Max(); + +// for (int i = _keyActions.Count(); i < max; i++) +// { +// _keyActions.Add(new Pause()); +// } + +// for (int i = _pointerActions.Count(); i < max; i++) +// { +// _pointerActions.Add(new Pause()); +// } + +// for (int i = _wheelActions.Count(); i < max; i++) +// { +// _wheelActions.Add(new Pause()); +// } + +// for (int i = _noneActions.Count(); i < max; i++) +// { +// _noneActions.Add(new Pause()); +// } + +// return this; +// } + +// public IEnumerator GetEnumerator() +// { +// var sourceActions = new List +// { +// _keyActions, +// _pointerActions, +// _wheelActions, +// _noneActions +// }; +// return sourceActions.GetEnumerator(); +// } + +// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); +//} + +//public record PointerDownOptions : IPointerCommonProperties +//{ +// public int? Width { get; set; } +// public int? Height { get; set; } +// public double? Pressure { get; set; } +// public double? TangentialPressure { get; set; } +// public int? Twist { get; set; } +// public double? AltitudeAngle { get; set; } +// public double? AzimuthAngle { get; set; } +//} + +//public record PointerMoveOptions : IPointerCommonProperties +//{ +// public int? Duration { get; set; } +// public Origin? Origin { get; set; } + +// public int? Width { get; set; } +// public int? Height { get; set; } +// public double? Pressure { get; set; } +// public double? TangentialPressure { get; set; } +// public int? Twist { get; set; } +// public double? AltitudeAngle { get; set; } +// public double? AzimuthAngle { get; set; } +//} diff --git a/dotnet/src/webdriver/BiDi/Modules/Input/SourceActions.cs b/dotnet/src/webdriver/BiDi/Modules/Input/SourceActions.cs new file mode 100644 index 0000000000000..a9b7ed3f7001f --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Modules/Input/SourceActions.cs @@ -0,0 +1,155 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Text.Json.Serialization; + +#nullable enable + +namespace OpenQA.Selenium.BiDi.Modules.Input; + +public abstract record SourceActions +{ + public string Id { get; } = Guid.NewGuid().ToString(); +} + +public interface ISourceAction; + +public record SourceActions : SourceActions, IEnumerable where T : ISourceAction +{ + public IList Actions { get; set; } = []; + + public IEnumerator GetEnumerator() => Actions.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => Actions.GetEnumerator(); + + public void Add(ISourceAction action) => Actions.Add(action); +} + +[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] +[JsonDerivedType(typeof(Pause), "pause")] +[JsonDerivedType(typeof(Key.Down), "keyDown")] +[JsonDerivedType(typeof(Key.Up), "keyUp")] +public interface IKeySourceAction : ISourceAction; + +public record KeyActions : SourceActions +{ + public KeyActions Type(string text) + { + foreach (var character in text) + { + Add(new Key.Down(character)); + Add(new Key.Up(character)); + } + + return this; + } +} + +[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] +[JsonDerivedType(typeof(Pause), "pause")] +[JsonDerivedType(typeof(Pointer.Down), "pointerDown")] +[JsonDerivedType(typeof(Pointer.Up), "pointerUp")] +[JsonDerivedType(typeof(Pointer.Move), "pointerMove")] +public interface IPointerSourceAction : ISourceAction; + +public record PointerActions : SourceActions +{ + public PointerParameters? Options { get; set; } +} + +[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] +[JsonDerivedType(typeof(Pause), "pause")] +[JsonDerivedType(typeof(Wheel.Scroll), "scroll")] +public interface IWheelSourceAction : ISourceAction; + +public record WheelActions : SourceActions; + +[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] +[JsonDerivedType(typeof(Pause), "pause")] +public interface INoneSourceAction : ISourceAction; + +public record NoneActions : SourceActions; + +public abstract partial record Key : IKeySourceAction +{ + public record Down(char Value) : Key; + + public record Up(char Value) : Key; +} + +public abstract record Pointer : IPointerSourceAction +{ + public record Down(int Button) : Pointer, IPointerCommonProperties + { + public int? Width { get; set; } + public int? Height { get; set; } + public double? Pressure { get; set; } + public double? TangentialPressure { get; set; } + public int? Twist { get; set; } + public double? AltitudeAngle { get; set; } + public double? AzimuthAngle { get; set; } + } + + public record Up(int Button) : Pointer; + + public record Move(int X, int Y) : Pointer, IPointerCommonProperties + { + public int? Duration { get; set; } + + public Origin? Origin { get; set; } + + public int? Width { get; set; } + public int? Height { get; set; } + public double? Pressure { get; set; } + public double? TangentialPressure { get; set; } + public int? Twist { get; set; } + public double? AltitudeAngle { get; set; } + public double? AzimuthAngle { get; set; } + } +} + +public abstract record Wheel : IWheelSourceAction +{ + public record Scroll(int X, int Y, int DeltaX, int DeltaY) : Wheel + { + public int? Duration { get; set; } + + public Origin? Origin { get; set; } + } +} + +public abstract record None : INoneSourceAction; + +public record Pause : ISourceAction, IKeySourceAction, IPointerSourceAction, IWheelSourceAction, INoneSourceAction +{ + public long? Duration { get; set; } +} + +public record PointerParameters +{ + public PointerType? PointerType { get; set; } +} + +public enum PointerType +{ + Mouse, + Pen, + Touch +} + +public interface IPointerCommonProperties +{ + public int? Width { get; set; } + + public int? Height { get; set; } + + public double? Pressure { get; set; } + + public double? TangentialPressure { get; set; } + + public int? Twist { get; set; } + + public double? AltitudeAngle { get; set; } + + public double? AzimuthAngle { get; set; } +} diff --git a/dotnet/src/webdriver/BiDi/Modules/Log/Entry.cs b/dotnet/src/webdriver/BiDi/Modules/Log/Entry.cs new file mode 100644 index 0000000000000..dba3714e27403 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Modules/Log/Entry.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; + +#nullable enable + +namespace OpenQA.Selenium.BiDi.Modules.Log; + +// https://github.com/dotnet/runtime/issues/72604 +//[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] +//[JsonDerivedType(typeof(Console), "console")] +//[JsonDerivedType(typeof(Javascript), "javascript")] +public abstract record Entry(BiDi BiDi, Level Level, Script.Source Source, string Text, DateTimeOffset Timestamp) + : EventArgs(BiDi) +{ + public Script.StackTrace? StackTrace { get; set; } + + public record Console(BiDi BiDi, Level Level, Script.Source Source, string Text, DateTimeOffset Timestamp, string Method, IReadOnlyList Args) + : Entry(BiDi, Level, Source, Text, Timestamp); + + public record Javascript(BiDi BiDi, Level Level, Script.Source Source, string Text, DateTimeOffset Timestamp) + : Entry(BiDi, Level, Source, Text, Timestamp); +} + +public enum Level +{ + Debug, + Info, + Warn, + Error +} diff --git a/dotnet/src/webdriver/BiDi/Modules/Log/LogEntry.cs b/dotnet/src/webdriver/BiDi/Modules/Log/LogEntry.cs deleted file mode 100644 index f210c126a8859..0000000000000 --- a/dotnet/src/webdriver/BiDi/Modules/Log/LogEntry.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace OpenQA.Selenium.BiDi.Modules.Log; - -// https://github.com/dotnet/runtime/issues/72604 -//[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -//[JsonDerivedType(typeof(ConsoleLogEntry), "console")] -//[JsonDerivedType(typeof(JavascriptLogEntry), "javascript")] -public abstract record BaseLogEntry(BiDi BiDi, Level Level, Script.Source Source, string Text, DateTimeOffset Timestamp) - : EventArgs(BiDi); - -public record ConsoleLogEntry(BiDi BiDi, Level Level, Script.Source Source, string Text, DateTimeOffset Timestamp, string Method, IReadOnlyList Args) - : BaseLogEntry(BiDi, Level, Source, Text, Timestamp); - -public record JavascriptLogEntry(BiDi BiDi, Level Level, Script.Source Source, string Text, DateTimeOffset Timestamp) - : BaseLogEntry(BiDi, Level, Source, Text, Timestamp); - -public enum Level -{ - Debug, - Info, - Warn, - Error -} diff --git a/dotnet/src/webdriver/BiDi/Modules/Log/LogModule.cs b/dotnet/src/webdriver/BiDi/Modules/Log/LogModule.cs index 7bf84d530126a..aeb09a08754bd 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Log/LogModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Log/LogModule.cs @@ -2,16 +2,18 @@ using System; using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Log; public sealed class LogModule(Broker broker) : Module(broker) { - public async Task OnEntryAddedAsync(Func handler, SubscriptionOptions? options = null) + public async Task OnEntryAddedAsync(Func handler, SubscriptionOptions? options = null) { return await Broker.SubscribeAsync("log.entryAdded", handler, options).ConfigureAwait(false); } - public async Task OnEntryAddedAsync(Action handler, SubscriptionOptions? options = null) + public async Task OnEntryAddedAsync(Action handler, SubscriptionOptions? options = null) { return await Broker.SubscribeAsync("log.entryAdded", handler, options).ConfigureAwait(false); } diff --git a/dotnet/src/webdriver/BiDi/Modules/Module.cs b/dotnet/src/webdriver/BiDi/Modules/Module.cs index a6ac7d1719c43..a7c2491349750 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Module.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Module.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules; public abstract class Module(Broker broker) diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/AddInterceptCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Network/AddInterceptCommand.cs index 4856193d512d9..219f3640ca466 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/AddInterceptCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/AddInterceptCommand.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; internal class AddInterceptCommand(AddInterceptCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/AuthChallenge.cs b/dotnet/src/webdriver/BiDi/Modules/Network/AuthChallenge.cs index b304cbacf3f1e..f85da002f189e 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/AuthChallenge.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/AuthChallenge.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record AuthChallenge(string Scheme, string Realm); diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/AuthCredentials.cs b/dotnet/src/webdriver/BiDi/Modules/Network/AuthCredentials.cs index 384360e660f44..8f78b5465f969 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/AuthCredentials.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/AuthCredentials.cs @@ -1,12 +1,14 @@ using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; [JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -[JsonDerivedType(typeof(BasicAuthCredentials), "password")] +[JsonDerivedType(typeof(Basic), "password")] public abstract record AuthCredentials { - public static BasicAuthCredentials Basic(string username, string password) => new(username, password); + public record Basic(string Username, string Password) : AuthCredentials; } -public record BasicAuthCredentials(string Username, string Password) : AuthCredentials; + diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/AuthRequiredEventArgs.cs b/dotnet/src/webdriver/BiDi/Modules/Network/AuthRequiredEventArgs.cs index 6e3e8626e8b5f..dca341b07b4da 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/AuthRequiredEventArgs.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/AuthRequiredEventArgs.cs @@ -1,5 +1,7 @@ using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record AuthRequiredEventArgs(BiDi BiDi, BrowsingContext.BrowsingContext Context, bool IsBlocked, BrowsingContext.Navigation Navigation, long RedirectCount, RequestData Request, DateTimeOffset Timestamp, ResponseData Response) : diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/BaseParametersEventArgs.cs b/dotnet/src/webdriver/BiDi/Modules/Network/BaseParametersEventArgs.cs index d632e433e137d..e8e721d033ca5 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/BaseParametersEventArgs.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/BaseParametersEventArgs.cs @@ -2,6 +2,8 @@ using System.Text.Json.Serialization; using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public abstract record BaseParametersEventArgs(BiDi BiDi, BrowsingContext.BrowsingContext Context, bool IsBlocked, BrowsingContext.Navigation Navigation, long RedirectCount, RequestData Request, DateTimeOffset Timestamp) diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/BeforeRequestSentEventArgs.cs b/dotnet/src/webdriver/BiDi/Modules/Network/BeforeRequestSentEventArgs.cs index 0ca4994bd7bae..53f4b85aa3669 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/BeforeRequestSentEventArgs.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/BeforeRequestSentEventArgs.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Modules.BrowsingContext; using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record BeforeRequestSentEventArgs(BiDi BiDi, BrowsingContext.BrowsingContext Context, bool IsBlocked, Navigation Navigation, long RedirectCount, RequestData Request, DateTimeOffset Timestamp, Initiator Initiator) diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/BytesValue.cs b/dotnet/src/webdriver/BiDi/Modules/Network/BytesValue.cs index 02b982422d4c7..6c5d88a930d66 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/BytesValue.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/BytesValue.cs @@ -1,15 +1,17 @@ using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; [JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -[JsonDerivedType(typeof(StringValue), "string")] -[JsonDerivedType(typeof(Base64Value), "base64")] +[JsonDerivedType(typeof(String), "string")] +[JsonDerivedType(typeof(Base64), "base64")] public abstract record BytesValue { - public static implicit operator BytesValue(string value) => new StringValue(value); -} + public static implicit operator BytesValue(string value) => new String(value); -public record StringValue(string Value) : BytesValue; + public record String(string Value) : BytesValue; -public record Base64Value(string Value) : BytesValue; + public record Base64(string Value) : BytesValue; +} diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/ContinueRequestCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Network/ContinueRequestCommand.cs index bdf572ece2c08..447dfa41baa70 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/ContinueRequestCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/ContinueRequestCommand.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Communication; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; internal class ContinueRequestCommand(ContinueRequestCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/ContinueResponseCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Network/ContinueResponseCommand.cs index 6762035e94e38..ba2e4e60a1bed 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/ContinueResponseCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/ContinueResponseCommand.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Communication; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; internal class ContinueResponseCommand(ContinueResponseCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/ContinueWithAuthCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Network/ContinueWithAuthCommand.cs index 4c6553181da92..c486066015ec4 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/ContinueWithAuthCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/ContinueWithAuthCommand.cs @@ -1,21 +1,24 @@ using OpenQA.Selenium.BiDi.Communication; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; internal class ContinueWithAuthCommand(ContinueWithAuthParameters @params) : Command(@params); [JsonPolymorphic(TypeDiscriminatorPropertyName = "action")] -[JsonDerivedType(typeof(ContinueWithAuthCredentials), "provideCredentials")] -[JsonDerivedType(typeof(ContinueWithDefaultAuth), "default")] -[JsonDerivedType(typeof(ContinueWithCancelledAuth), "cancel")] -internal abstract record ContinueWithAuthParameters(Request Request) : CommandParameters; - -internal record ContinueWithAuthCredentials(Request Request, AuthCredentials Credentials) : ContinueWithAuthParameters(Request); +[JsonDerivedType(typeof(Credentials), "provideCredentials")] +[JsonDerivedType(typeof(Default), "default")] +[JsonDerivedType(typeof(Cancel), "cancel")] +internal abstract record ContinueWithAuthParameters(Request Request) : CommandParameters +{ + internal record Credentials(Request Request, [property: JsonPropertyName("credentials")] AuthCredentials AuthCredentials) : ContinueWithAuthParameters(Request); -internal record ContinueWithDefaultAuth(Request Request) : ContinueWithAuthParameters(Request); + internal record Default(Request Request) : ContinueWithAuthParameters(Request); -internal record ContinueWithCancelledAuth(Request Request) : ContinueWithAuthParameters(Request); + internal record Cancel(Request Request) : ContinueWithAuthParameters(Request); +} public record ContinueWithAuthOptions : CommandOptions; diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/Cookie.cs b/dotnet/src/webdriver/BiDi/Modules/Network/Cookie.cs index 0c615762e7044..f3a4bedc8f926 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/Cookie.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/Cookie.cs @@ -1,6 +1,8 @@ using System; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record Cookie(string Name, BytesValue Value, string Domain, string Path, long Size, bool HttpOnly, bool Secure, SameSite SameSite) diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/CookieHeader.cs b/dotnet/src/webdriver/BiDi/Modules/Network/CookieHeader.cs index dac9bd7b8260a..e588a87d0acb8 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/CookieHeader.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/CookieHeader.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record CookieHeader(string Name, BytesValue Value); diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/FailRequestCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Network/FailRequestCommand.cs index fe25e4904e34f..f3b01ee692743 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/FailRequestCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/FailRequestCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; internal class FailRequestCommand(FailRequestCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/FetchErrorEventArgs.cs b/dotnet/src/webdriver/BiDi/Modules/Network/FetchErrorEventArgs.cs index ce77fbed5216c..6c911e0009fa7 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/FetchErrorEventArgs.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/FetchErrorEventArgs.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Modules.BrowsingContext; using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record FetchErrorEventArgs(BiDi BiDi, BrowsingContext.BrowsingContext Context, bool IsBlocked, Navigation Navigation, long RedirectCount, RequestData Request, DateTimeOffset Timestamp, string ErrorText) diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/FetchTimingInfo.cs b/dotnet/src/webdriver/BiDi/Modules/Network/FetchTimingInfo.cs index d1b8caf24e5ca..d5980cc2ca141 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/FetchTimingInfo.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/FetchTimingInfo.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record FetchTimingInfo(double TimeOrigin, diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/Header.cs b/dotnet/src/webdriver/BiDi/Modules/Network/Header.cs index d32397f88dbd3..0804a1919dec0 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/Header.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/Header.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record Header(string Name, BytesValue Value); diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/Initiator.cs b/dotnet/src/webdriver/BiDi/Modules/Network/Initiator.cs index 128b69f5097cf..a1449e77928a2 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/Initiator.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/Initiator.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record Initiator(InitiatorType Type) diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/Intercept.cs b/dotnet/src/webdriver/BiDi/Modules/Network/Intercept.cs index 26d71006cc03f..4fd55a7d14b0f 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/Intercept.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/Intercept.cs @@ -3,39 +3,41 @@ using System.Linq; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public class Intercept : IAsyncDisposable { private readonly BiDi _bidi; - protected readonly IList _onBeforeRequestSentSubscriptions = []; - protected readonly IList _onResponseStartedSubscriptions = []; - protected readonly IList _onAuthRequiredSubscriptions = []; - internal Intercept(BiDi bidi, string id) { _bidi = bidi; Id = id; } - public string Id { get; } + internal string Id { get; } + + protected IList OnBeforeRequestSentSubscriptions { get; } = []; + protected IList OnResponseStartedSubscriptions { get; } = []; + protected IList OnAuthRequiredSubscriptions { get; } = []; public async Task RemoveAsync() { await _bidi.Network.RemoveInterceptAsync(this).ConfigureAwait(false); - foreach (var subscription in _onBeforeRequestSentSubscriptions) + foreach (var subscription in OnBeforeRequestSentSubscriptions) { await subscription.UnsubscribeAsync().ConfigureAwait(false); } - foreach (var subscription in _onResponseStartedSubscriptions) + foreach (var subscription in OnResponseStartedSubscriptions) { await subscription.UnsubscribeAsync().ConfigureAwait(false); } - foreach (var subscription in _onAuthRequiredSubscriptions) + foreach (var subscription in OnAuthRequiredSubscriptions) { await subscription.UnsubscribeAsync().ConfigureAwait(false); } @@ -45,21 +47,21 @@ public async Task OnBeforeRequestSentAsync(Func await Filter(args, handler), options).ConfigureAwait(false); - _onBeforeRequestSentSubscriptions.Add(subscription); + OnBeforeRequestSentSubscriptions.Add(subscription); } public async Task OnResponseStartedAsync(Func handler, SubscriptionOptions? options = null) { var subscription = await _bidi.Network.OnResponseStartedAsync(async args => await Filter(args, handler), options).ConfigureAwait(false); - _onResponseStartedSubscriptions.Add(subscription); + OnResponseStartedSubscriptions.Add(subscription); } public async Task OnAuthRequiredAsync(Func handler, SubscriptionOptions? options = null) { var subscription = await _bidi.Network.OnAuthRequiredAsync(async args => await Filter(args, handler), options).ConfigureAwait(false); - _onAuthRequiredSubscriptions.Add(subscription); + OnAuthRequiredSubscriptions.Add(subscription); } private async Task Filter(BeforeRequestSentEventArgs args, Func handler) diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/NetworkModule.cs b/dotnet/src/webdriver/BiDi/Modules/Network/NetworkModule.cs index 65264dbf51a67..7d4eac19a673f 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/NetworkModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/NetworkModule.cs @@ -3,6 +3,8 @@ using System.Threading.Tasks; using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public sealed class NetworkModule(Broker broker) : Module(broker) @@ -47,7 +49,7 @@ public async Task InterceptResponseAsync(Func InterceptAuthenticationAsync(Func handler, AddInterceptOptions? interceptOptions = null, SubscriptionOptions? options = null) + public async Task InterceptAuthAsync(Func handler, AddInterceptOptions? interceptOptions = null, SubscriptionOptions? options = null) { var intercept = await AddInterceptAsync([InterceptPhase.AuthRequired], interceptOptions).ConfigureAwait(false); @@ -113,17 +115,17 @@ internal async Task ProvideResponseAsync(Request request, ProvideResponseOptions internal async Task ContinueWithAuthAsync(Request request, AuthCredentials credentials, ContinueWithAuthOptions? options = null) { - await Broker.ExecuteCommandAsync(new ContinueWithAuthCommand(new ContinueWithAuthCredentials(request, credentials)), options).ConfigureAwait(false); + await Broker.ExecuteCommandAsync(new ContinueWithAuthCommand(new ContinueWithAuthParameters.Credentials(request, credentials)), options).ConfigureAwait(false); } internal async Task ContinueWithAuthAsync(Request request, ContinueWithDefaultAuthOptions? options = null) { - await Broker.ExecuteCommandAsync(new ContinueWithAuthCommand(new ContinueWithDefaultAuth(request)), options).ConfigureAwait(false); + await Broker.ExecuteCommandAsync(new ContinueWithAuthCommand(new ContinueWithAuthParameters.Default(request)), options).ConfigureAwait(false); } internal async Task ContinueWithAuthAsync(Request request, ContinueWithCancelledAuthOptions? options = null) { - await Broker.ExecuteCommandAsync(new ContinueWithAuthCommand(new ContinueWithCancelledAuth(request)), options).ConfigureAwait(false); + await Broker.ExecuteCommandAsync(new ContinueWithAuthCommand(new ContinueWithAuthParameters.Cancel(request)), options).ConfigureAwait(false); } public async Task OnBeforeRequestSentAsync(Func handler, SubscriptionOptions? options = null) @@ -166,7 +168,12 @@ public async Task OnFetchErrorAsync(Action ha return await Broker.SubscribeAsync("network.fetchError", handler, options).ConfigureAwait(false); } - internal async Task OnAuthRequiredAsync(Func handler, SubscriptionOptions? options = null) + public async Task OnAuthRequiredAsync(Func handler, SubscriptionOptions? options = null) + { + return await Broker.SubscribeAsync("network.authRequired", handler, options).ConfigureAwait(false); + } + + public async Task OnAuthRequiredAsync(Action handler, SubscriptionOptions? options = null) { return await Broker.SubscribeAsync("network.authRequired", handler, options).ConfigureAwait(false); } diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/ProvideResponseCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Network/ProvideResponseCommand.cs index 317ac4474b4fa..6dae6fc24556f 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/ProvideResponseCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/ProvideResponseCommand.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Communication; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; internal class ProvideResponseCommand(ProvideResponseCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/RemoveInterceptCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Network/RemoveInterceptCommand.cs index de64aa1200fb7..c592b74e13a1a 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/RemoveInterceptCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/RemoveInterceptCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; internal class RemoveInterceptCommand(RemoveInterceptCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/Request.cs b/dotnet/src/webdriver/BiDi/Modules/Network/Request.cs index 1b68df0768897..f7b93fbebff19 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/Request.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/Request.cs @@ -1,5 +1,7 @@ using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public class Request diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/RequestData.cs b/dotnet/src/webdriver/BiDi/Modules/Network/RequestData.cs index 442db03520234..01cc77f040e79 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/RequestData.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/RequestData.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record RequestData(Request Request, string Url, string Method, IReadOnlyList
Headers, IReadOnlyList Cookies, long HeadersSize, long? BodySize, FetchTimingInfo Timings); diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/ResponseCompletedEventArgs.cs b/dotnet/src/webdriver/BiDi/Modules/Network/ResponseCompletedEventArgs.cs index 81fec792efdfd..75c9aa21332bd 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/ResponseCompletedEventArgs.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/ResponseCompletedEventArgs.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Modules.BrowsingContext; using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record ResponseCompletedEventArgs(BiDi BiDi, BrowsingContext.BrowsingContext Context, bool IsBlocked, Navigation Navigation, long RedirectCount, RequestData Request, DateTimeOffset Timestamp, ResponseData Response) diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/ResponseContent.cs b/dotnet/src/webdriver/BiDi/Modules/Network/ResponseContent.cs index 9647356e7b052..5511c4b24129f 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/ResponseContent.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/ResponseContent.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record ResponseContent(long Size); diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/ResponseData.cs b/dotnet/src/webdriver/BiDi/Modules/Network/ResponseData.cs index 5ac779301b9ab..3d21c2c202fe8 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/ResponseData.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/ResponseData.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record ResponseData(string Url, @@ -9,7 +11,7 @@ public record ResponseData(string Url, string StatusText, bool FromCache, IReadOnlyList
Headers, - string MymeType, + string MimeType, long BytesReceived, long? HeadersSize, long? BodySize, diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/ResponseStartedEventArgs.cs b/dotnet/src/webdriver/BiDi/Modules/Network/ResponseStartedEventArgs.cs index 0c80739bf4c72..aa3aff6fd5cf9 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/ResponseStartedEventArgs.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/ResponseStartedEventArgs.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Modules.BrowsingContext; using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record ResponseStartedEventArgs(BiDi BiDi, BrowsingContext.BrowsingContext Context, bool IsBlocked, Navigation Navigation, long RedirectCount, RequestData Request, DateTimeOffset Timestamp, ResponseData Response) diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/SetCookieHeader.cs b/dotnet/src/webdriver/BiDi/Modules/Network/SetCookieHeader.cs index d11d8ee8a21e5..a73c9171419f0 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/SetCookieHeader.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/SetCookieHeader.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; public record SetCookieHeader(string Name, BytesValue Value) diff --git a/dotnet/src/webdriver/BiDi/Modules/Network/UrlPattern.cs b/dotnet/src/webdriver/BiDi/Modules/Network/UrlPattern.cs index 50226a115f327..30a034fbee4d0 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Network/UrlPattern.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Network/UrlPattern.cs @@ -1,31 +1,31 @@ using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Network; [JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -[JsonDerivedType(typeof(UrlPatternPattern), "pattern")] -[JsonDerivedType(typeof(UrlPatternString), "string")] +[JsonDerivedType(typeof(Pattern), "pattern")] +[JsonDerivedType(typeof(String), "string")] public abstract record UrlPattern { - public static UrlPatternPattern Patter(string? protocol = null, string? hostname = null, string? port = null, string? pathname = null, string? search = null) - => new() { Protocol = protocol, Hostname = hostname, Port = port, Pathname = pathname, Search = search }; + public static implicit operator UrlPattern(string value) => new String(value); - public static UrlPatternString String(string pattern) => new UrlPatternString(pattern); + public record Pattern : UrlPattern + { + public string? Protocol { get; set; } - public static implicit operator UrlPattern(string value) => new UrlPatternString(value); -} + public string? Hostname { get; set; } -public record UrlPatternPattern : UrlPattern -{ - public string? Protocol { get; set; } + public string? Port { get; set; } - public string? Hostname { get; set; } + public string? Pathname { get; set; } - public string? Port { get; set; } + public string? Search { get; set; } + } - public string? Pathname { get; set; } - - public string? Search { get; set; } + public record String(string Pattern) : UrlPattern + { + public new string Pattern { get; } = Pattern; + } } - -public record UrlPatternString(string Pattern) : UrlPattern; diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/AddPreloadScriptCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Script/AddPreloadScriptCommand.cs index 516e65b10f45c..512d79a86900e 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/AddPreloadScriptCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/AddPreloadScriptCommand.cs @@ -1,13 +1,15 @@ using OpenQA.Selenium.BiDi.Communication; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; internal class AddPreloadScriptCommand(AddPreloadScriptCommandParameters @params) : Command(@params); internal record AddPreloadScriptCommandParameters(string FunctionDeclaration) : CommandParameters { - public IEnumerable? Arguments { get; set; } + public IEnumerable? Arguments { get; set; } public IEnumerable? Contexts { get; set; } @@ -24,7 +26,7 @@ internal AddPreloadScriptOptions(BrowsingContextAddPreloadScriptOptions? options Sandbox = options?.Sandbox; } - public IEnumerable? Arguments { get; set; } + public IEnumerable? Arguments { get; set; } public IEnumerable? Contexts { get; set; } @@ -33,7 +35,7 @@ internal AddPreloadScriptOptions(BrowsingContextAddPreloadScriptOptions? options public record BrowsingContextAddPreloadScriptOptions { - public IEnumerable? Arguments { get; set; } + public IEnumerable? Arguments { get; set; } public string? Sandbox { get; set; } } diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/CallFunctionCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Script/CallFunctionCommand.cs index 08c9800911044..9ff03a72896f4 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/CallFunctionCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/CallFunctionCommand.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Communication; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; internal class CallFunctionCommand(CallFunctionCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/Channel.cs b/dotnet/src/webdriver/BiDi/Modules/Script/Channel.cs index a828d9a4caff3..6518aee1d5388 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/Channel.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/Channel.cs @@ -1,14 +1,5 @@ -namespace OpenQA.Selenium.BiDi.Modules.Script; - -public class Channel -{ - readonly BiDi _bidi; +#nullable enable - internal Channel(BiDi bidi, string id) - { - _bidi = bidi; - Id = id; - } +namespace OpenQA.Selenium.BiDi.Modules.Script; - internal string Id { get; } -} +public record Channel(string Id); diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/ChannelValue.cs b/dotnet/src/webdriver/BiDi/Modules/Script/ChannelValue.cs deleted file mode 100644 index 4b92a7dc15b96..0000000000000 --- a/dotnet/src/webdriver/BiDi/Modules/Script/ChannelValue.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace OpenQA.Selenium.BiDi.Modules.Script; - -public record ChannelValue : LocalValue -{ - public string Type => "channel"; -} - -public record ChannelProperties(Channel Channel) -{ - public SerializationOptions? SerializationOptions { get; set; } - - public ResultOwnership? Ownership { get; set; } -} diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/DisownCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Script/DisownCommand.cs index dfe04fc14e0be..b09ab31da07b3 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/DisownCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/DisownCommand.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Communication; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; internal class DisownCommand(DisownCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/EvaluateCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Script/EvaluateCommand.cs index 307a9156e8b4b..8ccba001543af 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/EvaluateCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/EvaluateCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; internal class EvaluateCommand(EvaluateCommandParameters @params) : Command(@params); @@ -24,12 +26,16 @@ public record EvaluateOptions : CommandOptions // https://github.com/dotnet/runtime/issues/72604 //[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -//[JsonDerivedType(typeof(EvaluateResultSuccess), "success")] -//[JsonDerivedType(typeof(EvaluateResultException), "exception")] -public abstract record EvaluateResult; - -public record EvaluateResultSuccess(RemoteValue Result) : EvaluateResult; +//[JsonDerivedType(typeof(Success), "success")] +//[JsonDerivedType(typeof(Exception), "exception")] +public abstract record EvaluateResult +{ + public record Success(RemoteValue Result, Realm Realm) : EvaluateResult + { + public static implicit operator RemoteValue(Success success) => success.Result; + } -public record EvaluateResultException(ExceptionDetails ExceptionDetails) : EvaluateResult; + public record Exception(ExceptionDetails ExceptionDetails, Realm Realm) : EvaluateResult; +} public record ExceptionDetails(long ColumnNumber, long LineNumber, StackTrace StackTrace, string Text); diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/GetRealmsCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Script/GetRealmsCommand.cs index 98d4a03ba785f..e5730a9d21a06 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/GetRealmsCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/GetRealmsCommand.cs @@ -1,6 +1,9 @@ using OpenQA.Selenium.BiDi.Communication; +using System.Collections; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; internal class GetRealmsCommand(GetRealmsCommandParameters @params) : Command(@params); @@ -19,4 +22,20 @@ public record GetRealmsOptions : CommandOptions public RealmType? Type { get; set; } } -internal record GetRealmsResult(IReadOnlyList Realms); +public record GetRealmsResult : IReadOnlyList +{ + private readonly IReadOnlyList _realms; + + internal GetRealmsResult(IReadOnlyList realms) + { + _realms = realms; + } + + public RealmInfo this[int index] => _realms[index]; + + public int Count => _realms.Count; + + public IEnumerator GetEnumerator() => _realms.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => (_realms as IEnumerable).GetEnumerator(); +} diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/Handle.cs b/dotnet/src/webdriver/BiDi/Modules/Script/Handle.cs index 2e2649af926cf..8323bea292827 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/Handle.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/Handle.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public class Handle diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/InternalId.cs b/dotnet/src/webdriver/BiDi/Modules/Script/InternalId.cs index 89e08f58f9121..1a34d8bd61bb5 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/InternalId.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/InternalId.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public class InternalId diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/LocalValue.cs b/dotnet/src/webdriver/BiDi/Modules/Script/LocalValue.cs index 3451352ab962d..2b162c438dd3d 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/LocalValue.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/LocalValue.cs @@ -1,31 +1,36 @@ using System.Collections.Generic; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; [JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -[JsonDerivedType(typeof(NumberLocalValue), "number")] -[JsonDerivedType(typeof(StringLocalValue), "string")] -[JsonDerivedType(typeof(NullLocalValue), "null")] -[JsonDerivedType(typeof(UndefinedLocalValue), "undefined")] -[JsonDerivedType(typeof(ArrayLocalValue), "array")] -[JsonDerivedType(typeof(DateLocalValue), "date")] -[JsonDerivedType(typeof(MapLocalValue), "map")] -[JsonDerivedType(typeof(ObjectLocalValue), "object")] -[JsonDerivedType(typeof(RegExpLocalValue), "regexp")] -[JsonDerivedType(typeof(SetLocalValue), "set")] +[JsonDerivedType(typeof(Number), "number")] +[JsonDerivedType(typeof(String), "string")] +[JsonDerivedType(typeof(Null), "null")] +[JsonDerivedType(typeof(Undefined), "undefined")] +[JsonDerivedType(typeof(Channel), "channel")] +[JsonDerivedType(typeof(Array), "array")] +[JsonDerivedType(typeof(Date), "date")] +[JsonDerivedType(typeof(Map), "map")] +[JsonDerivedType(typeof(Object), "object")] +[JsonDerivedType(typeof(RegExp), "regexp")] +[JsonDerivedType(typeof(Set), "set")] public abstract record LocalValue { - public static implicit operator LocalValue(int value) { return new NumberLocalValue(value); } - public static implicit operator LocalValue(string value) { return new StringLocalValue(value); } + public static implicit operator LocalValue(int value) { return new Number(value); } + public static implicit operator LocalValue(string value) { return new String(value); } // TODO: Extend converting from types public static LocalValue ConvertFrom(object? value) { switch (value) { + case LocalValue: + return (LocalValue)value; case null: - return new NullLocalValue(); + return new Null(); case int: return (int)value; case string: @@ -43,41 +48,55 @@ public static LocalValue ConvertFrom(object? value) values.Add([property.Name, ConvertFrom(property.GetValue(value))]); } - return new ObjectLocalValue(values); + return new Object(values); } } } -} -public abstract record PrimitiveProtocolLocalValue : LocalValue -{ + public abstract record PrimitiveProtocolLocalValue : LocalValue + { -} + } -public record NumberLocalValue(long Value) : PrimitiveProtocolLocalValue -{ - public static explicit operator NumberLocalValue(int n) => new NumberLocalValue(n); -} + public record Number(long Value) : PrimitiveProtocolLocalValue + { + public static explicit operator Number(int n) => new Number(n); + } -public record StringLocalValue(string Value) : PrimitiveProtocolLocalValue; + public record String(string Value) : PrimitiveProtocolLocalValue; -public record NullLocalValue : PrimitiveProtocolLocalValue; + public record Null : PrimitiveProtocolLocalValue; -public record UndefinedLocalValue : PrimitiveProtocolLocalValue; + public record Undefined : PrimitiveProtocolLocalValue; -public record ArrayLocalValue(IEnumerable Value) : LocalValue; + public record Channel(Channel.ChannelProperties Value) : LocalValue + { + [JsonInclude] + internal string type = "channel"; -public record DateLocalValue(string Value) : LocalValue; + public record ChannelProperties(Script.Channel Channel) + { + public SerializationOptions? SerializationOptions { get; set; } -public record MapLocalValue(IDictionary Value) : LocalValue; // seems to implement IDictionary + public ResultOwnership? Ownership { get; set; } + } + } -public record ObjectLocalValue(IEnumerable> Value) : LocalValue; + public record Array(IEnumerable Value) : LocalValue; -public record RegExpLocalValue(RegExpValue Value) : LocalValue; + public record Date(string Value) : LocalValue; -public record RegExpValue(string Pattern) -{ - public string? Flags { get; set; } -} + public record Map(IDictionary Value) : LocalValue; // seems to implement IDictionary + + public record Object(IEnumerable> Value) : LocalValue; -public record SetLocalValue(IEnumerable Value) : LocalValue; + public record RegExp(RegExp.RegExpValue Value) : LocalValue + { + public record RegExpValue(string Pattern) + { + public string? Flags { get; set; } + } + } + + public record Set(IEnumerable Value) : LocalValue; +} diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/MessageEventArgs.cs b/dotnet/src/webdriver/BiDi/Modules/Script/MessageEventArgs.cs new file mode 100644 index 0000000000000..e6062a76262c1 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Modules/Script/MessageEventArgs.cs @@ -0,0 +1,5 @@ +#nullable enable + +namespace OpenQA.Selenium.BiDi.Modules.Script; + +public record MessageEventArgs(BiDi BiDi, Channel Channel, RemoteValue Data, Source Source) : EventArgs(BiDi); diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/NodeProperties.cs b/dotnet/src/webdriver/BiDi/Modules/Script/NodeProperties.cs index 87bf2680efa6f..5cc214fcd225c 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/NodeProperties.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/NodeProperties.cs @@ -1,6 +1,8 @@ using System.Collections.Generic; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public record NodeProperties(long NodeType, long ChildNodeCount) @@ -9,7 +11,7 @@ public record NodeProperties(long NodeType, long ChildNodeCount) public IReadOnlyDictionary? Attributes { get; internal set; } [JsonInclude] - public IReadOnlyList? Children { get; internal set; } + public IReadOnlyList? Children { get; internal set; } [JsonInclude] public string? LocalName { get; internal set; } @@ -24,5 +26,5 @@ public record NodeProperties(long NodeType, long ChildNodeCount) public string? NodeValue { get; internal set; } [JsonInclude] - public NodeRemoteValue? ShadowRoot { get; internal set; } + public RemoteValue.Node? ShadowRoot { get; internal set; } } diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/PreloadScript.cs b/dotnet/src/webdriver/BiDi/Modules/Script/PreloadScript.cs index a59b9c099f05d..1b9027f21cf39 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/PreloadScript.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/PreloadScript.cs @@ -1,6 +1,8 @@ using System; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public class PreloadScript : IAsyncDisposable @@ -17,7 +19,7 @@ public PreloadScript(BiDi bidi, string id) public Task RemoveAsync() { - return _bidi.ScriptModule.RemovePreloadScriptAsync(this); + return _bidi.Script.RemovePreloadScriptAsync(this); } public async ValueTask DisposeAsync() diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/Realm.cs b/dotnet/src/webdriver/BiDi/Modules/Script/Realm.cs index 0b8bf28fdf994..a690df5eabd91 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/Realm.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/Realm.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public class Realm diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/RealmDestroyedEventArgs.cs b/dotnet/src/webdriver/BiDi/Modules/Script/RealmDestroyedEventArgs.cs new file mode 100644 index 0000000000000..b5d30f09c5fd3 --- /dev/null +++ b/dotnet/src/webdriver/BiDi/Modules/Script/RealmDestroyedEventArgs.cs @@ -0,0 +1,5 @@ +#nullable enable + +namespace OpenQA.Selenium.BiDi.Modules.Script; + +public record RealmDestroyedEventArgs(BiDi BiDi, Realm Realm) : EventArgs(BiDi); diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/RealmInfo.cs b/dotnet/src/webdriver/BiDi/Modules/Script/RealmInfo.cs index 4d88aa5e7b25c..4715be57392a7 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/RealmInfo.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/RealmInfo.cs @@ -1,36 +1,37 @@ using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; // https://github.com/dotnet/runtime/issues/72604 //[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -//[JsonDerivedType(typeof(WindowRealmInfo), "window")] -//[JsonDerivedType(typeof(DedicatedWorkerRealmInfo), "dedicated-worker")] -//[JsonDerivedType(typeof(SharedWorkerRealmInfo), "shared-worker")] -//[JsonDerivedType(typeof(ServiceWorkerRealmInfo), "service-worker")] -//[JsonDerivedType(typeof(WorkerRealmInfo), "worker")] -//[JsonDerivedType(typeof(PaintWorkletRealmInfo), "paint-worklet")] -//[JsonDerivedType(typeof(AudioWorkletRealmInfo), "audio-worklet")] -//[JsonDerivedType(typeof(WorkletRealmInfo), "worklet")] -public abstract record RealmInfo(BiDi BiDi) : EventArgs(BiDi); - -public abstract record BaseRealmInfo(BiDi BiDi, Realm Realm, string Origin) : RealmInfo(BiDi); - -public record WindowRealmInfo(BiDi BiDi, Realm Realm, string Origin, BrowsingContext.BrowsingContext Context) : BaseRealmInfo(BiDi, Realm, Origin) +//[JsonDerivedType(typeof(Window), "window")] +//[JsonDerivedType(typeof(DedicatedWorker), "dedicated-worker")] +//[JsonDerivedType(typeof(SharedWorker), "shared-worker")] +//[JsonDerivedType(typeof(ServiceWorker), "service-worker")] +//[JsonDerivedType(typeof(Worker), "worker")] +//[JsonDerivedType(typeof(PaintWorklet), "paint-worklet")] +//[JsonDerivedType(typeof(AudioWorklet), "audio-worklet")] +//[JsonDerivedType(typeof(Worklet), "worklet")] +public abstract record RealmInfo(BiDi BiDi, Realm Realm, string Origin) : EventArgs(BiDi) { - public string? Sandbox { get; set; } -} + public record Window(BiDi BiDi, Realm Realm, string Origin, BrowsingContext.BrowsingContext Context) : RealmInfo(BiDi, Realm, Origin) + { + public string? Sandbox { get; set; } + } -public record DedicatedWorkerRealmInfo(BiDi BiDi, Realm Realm, string Origin, IReadOnlyList Owners) : BaseRealmInfo(BiDi, Realm, Origin); + public record DedicatedWorker(BiDi BiDi, Realm Realm, string Origin, IReadOnlyList Owners) : RealmInfo(BiDi, Realm, Origin); -public record SharedWorkerRealmInfo(BiDi BiDi, Realm Realm, string Origin) : BaseRealmInfo(BiDi, Realm, Origin); + public record SharedWorker(BiDi BiDi, Realm Realm, string Origin) : RealmInfo(BiDi, Realm, Origin); -public record ServiceWorkerRealmInfo(BiDi BiDi, Realm Realm, string Origin) : BaseRealmInfo(BiDi, Realm, Origin); + public record ServiceWorker(BiDi BiDi, Realm Realm, string Origin) : RealmInfo(BiDi, Realm, Origin); -public record WorkerRealmInfo(BiDi BiDi, Realm Realm, string Origin) : BaseRealmInfo(BiDi, Realm, Origin); + public record Worker(BiDi BiDi, Realm Realm, string Origin) : RealmInfo(BiDi, Realm, Origin); -public record PaintWorkletRealmInfo(BiDi BiDi, Realm Realm, string Origin) : BaseRealmInfo(BiDi, Realm, Origin); + public record PaintWorklet(BiDi BiDi, Realm Realm, string Origin) : RealmInfo(BiDi, Realm, Origin); -public record AudioWorkletRealmInfo(BiDi BiDi, Realm Realm, string Origin) : BaseRealmInfo(BiDi, Realm, Origin); + public record AudioWorklet(BiDi BiDi, Realm Realm, string Origin) : RealmInfo(BiDi, Realm, Origin); -public record WorkletRealmInfo(BiDi BiDi, Realm Realm, string Origin) : BaseRealmInfo(BiDi, Realm, Origin); + public record Worklet(BiDi BiDi, Realm Realm, string Origin) : RealmInfo(BiDi, Realm, Origin); +} diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/RealmType.cs b/dotnet/src/webdriver/BiDi/Modules/Script/RealmType.cs index 0db521054c4d9..08444b2666830 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/RealmType.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/RealmType.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public enum RealmType diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/RemoteReference.cs b/dotnet/src/webdriver/BiDi/Modules/Script/RemoteReference.cs index f936501b0cb14..ac64940070e7e 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/RemoteReference.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/RemoteReference.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public abstract record RemoteReference : LocalValue; diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/RemoteValue.cs b/dotnet/src/webdriver/BiDi/Modules/Script/RemoteValue.cs index 0ce3be3404fa5..074ab2570b43e 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/RemoteValue.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/RemoteValue.cs @@ -1,44 +1,49 @@ using System; using System.Collections.Generic; +using System.Text.Json; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; // https://github.com/dotnet/runtime/issues/72604 //[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -//[JsonDerivedType(typeof(NumberRemoteValue), "number")] -//[JsonDerivedType(typeof(StringRemoteValue), "string")] -//[JsonDerivedType(typeof(NullRemoteValue), "null")] -//[JsonDerivedType(typeof(UndefinedRemoteValue), "undefined")] -//[JsonDerivedType(typeof(SymbolRemoteValue), "symbol")] -//[JsonDerivedType(typeof(ObjectRemoteValue), "object")] -//[JsonDerivedType(typeof(FunctionRemoteValue), "function")] -//[JsonDerivedType(typeof(RegExpRemoteValue), "regexp")] -//[JsonDerivedType(typeof(DateRemoteValue), "date")] -//[JsonDerivedType(typeof(MapRemoteValue), "map")] -//[JsonDerivedType(typeof(SetRemoteValue), "set")] -//[JsonDerivedType(typeof(WeakMapRemoteValue), "weakmap")] -//[JsonDerivedType(typeof(WeakSetRemoteValue), "weakset")] -//[JsonDerivedType(typeof(GeneratorRemoteValue), "generator")] -//[JsonDerivedType(typeof(ErrorRemoteValue), "error")] -//[JsonDerivedType(typeof(ProxyRemoteValue), "proxy")] -//[JsonDerivedType(typeof(PromiseRemoteValue), "promise")] -//[JsonDerivedType(typeof(TypedArrayRemoteValue), "typedarray")] -//[JsonDerivedType(typeof(ArrayBufferRemoteValue), "arraybuffer")] -//[JsonDerivedType(typeof(NodeListRemoteValue), "nodelist")] -//[JsonDerivedType(typeof(HtmlCollectionRemoteValue), "htmlcollection")] -//[JsonDerivedType(typeof(NodeRemoteValue), "node")] -//[JsonDerivedType(typeof(WindowProxyRemoteValue), "window")] +//[JsonDerivedType(typeof(Number), "number")] +//[JsonDerivedType(typeof(Boolean), "boolean")] +//[JsonDerivedType(typeof(String), "string")] +//[JsonDerivedType(typeof(Null), "null")] +//[JsonDerivedType(typeof(Undefined), "undefined")] +//[JsonDerivedType(typeof(Symbol), "symbol")] +//[JsonDerivedType(typeof(Array), "array")] +//[JsonDerivedType(typeof(Object), "object")] +//[JsonDerivedType(typeof(Function), "function")] +//[JsonDerivedType(typeof(RegExp), "regexp")] +//[JsonDerivedType(typeof(Date), "date")] +//[JsonDerivedType(typeof(Map), "map")] +//[JsonDerivedType(typeof(Set), "set")] +//[JsonDerivedType(typeof(WeakMap), "weakmap")] +//[JsonDerivedType(typeof(WeakSet), "weakset")] +//[JsonDerivedType(typeof(Generator), "generator")] +//[JsonDerivedType(typeof(Error), "error")] +//[JsonDerivedType(typeof(Proxy), "proxy")] +//[JsonDerivedType(typeof(Promise), "promise")] +//[JsonDerivedType(typeof(TypedArray), "typedarray")] +//[JsonDerivedType(typeof(ArrayBuffer), "arraybuffer")] +//[JsonDerivedType(typeof(NodeList), "nodelist")] +//[JsonDerivedType(typeof(HtmlCollection), "htmlcollection")] +//[JsonDerivedType(typeof(Node), "node")] +//[JsonDerivedType(typeof(WindowProxy), "window")] public abstract record RemoteValue { - public static implicit operator int(RemoteValue remoteValue) => (int)((NumberRemoteValue)remoteValue).Value; - public static implicit operator long(RemoteValue remoteValue) => ((NumberRemoteValue)remoteValue).Value; + public static implicit operator int(RemoteValue remoteValue) => (int)((Number)remoteValue).Value; + public static implicit operator long(RemoteValue remoteValue) => ((Number)remoteValue).Value; public static implicit operator string(RemoteValue remoteValue) { return remoteValue switch { - StringRemoteValue stringValue => stringValue.Value, - NullRemoteValue => null!, + String stringValue => stringValue.Value, + Null => null!, _ => throw new BiDiException($"Cannot convert {remoteValue} to string") }; } @@ -48,13 +53,17 @@ public static implicit operator string(RemoteValue remoteValue) { var type = typeof(TResult); + if (type == typeof(bool)) + { + return (TResult)(Convert.ToBoolean(((Boolean)this).Value) as object); + } if (type == typeof(int)) { - return (TResult)(Convert.ToInt32(((NumberRemoteValue)this).Value) as object); + return (TResult)(Convert.ToInt32(((Number)this).Value) as object); } else if (type == typeof(string)) { - return (TResult)(((StringRemoteValue)this).Value as object); + return (TResult)(((String)this).Value as object); } else if (type is object) { @@ -64,177 +73,184 @@ public static implicit operator string(RemoteValue remoteValue) throw new BiDiException("Cannot convert ....."); } -} -public abstract record PrimitiveProtocolRemoteValue : RemoteValue; + public record Number(long Value) : PrimitiveProtocolRemoteValue; -public record NumberRemoteValue(long Value) : PrimitiveProtocolRemoteValue; + public record Boolean(bool Value) : PrimitiveProtocolRemoteValue; -public record StringRemoteValue(string Value) : PrimitiveProtocolRemoteValue; + public record String(string Value) : PrimitiveProtocolRemoteValue; -public record NullRemoteValue : PrimitiveProtocolRemoteValue; + public record Null : PrimitiveProtocolRemoteValue; -public record UndefinedRemoteValue : PrimitiveProtocolRemoteValue; + public record Undefined : PrimitiveProtocolRemoteValue; -public record SymbolRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public record Symbol : RemoteValue + { + public Handle? Handle { get; set; } - public InternalId? InternalId { get; set; } -} + public InternalId? InternalId { get; set; } + } -public record ArrayRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public record Array : RemoteValue + { + public Handle? Handle { get; set; } - public InternalId? InternalId { get; set; } + public InternalId? InternalId { get; set; } - public IReadOnlyList? Value { get; set; } -} + public IReadOnlyList? Value { get; set; } + } -public record ObjectRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public record Object : RemoteValue + { + public Handle? Handle { get; set; } - public InternalId? InternalId { get; set; } + public InternalId? InternalId { get; set; } - public IReadOnlyList>? Value { get; set; } -} + public IReadOnlyList>? Value { get; set; } + } -public record FunctionRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public record Function : RemoteValue + { + public Handle? Handle { get; set; } - public InternalId? InternalId { get; set; } -} + public InternalId? InternalId { get; set; } + } -public record RegExpRemoteValue(RegExpValue Value) : RemoteValue -{ - public Handle? Handle { get; set; } + public record RegExp(RegExp.RegExpValue Value) : RemoteValue + { + public Handle? Handle { get; set; } - public InternalId? InternalId { get; set; } -} + public InternalId? InternalId { get; set; } -public record DateRemoteValue(string Value) : RemoteValue -{ - public Handle? Handle { get; set; } + public record RegExpValue(string Pattern) + { + public string? Flags { get; set; } + } + } - public InternalId? InternalId { get; set; } -} + public record Date(string Value) : RemoteValue + { + public Handle? Handle { get; set; } -public record MapRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public InternalId? InternalId { get; set; } + } - public InternalId? InternalId { get; set; } + public record Map : RemoteValue + { + public Handle? Handle { get; set; } - public IDictionary? Value { get; set; } -} + public InternalId? InternalId { get; set; } -public record SetRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public IDictionary? Value { get; set; } + } - public InternalId? InternalId { get; set; } + public record Set : RemoteValue + { + public Handle? Handle { get; set; } - public IReadOnlyList? Value { get; set; } -} + public InternalId? InternalId { get; set; } -public record WeakMapRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public IReadOnlyList? Value { get; set; } + } - public InternalId? InternalId { get; set; } -} + public record WeakMap : RemoteValue + { + public Handle? Handle { get; set; } -public record WeakSetRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public InternalId? InternalId { get; set; } + } - public InternalId? InternalId { get; set; } -} + public record WeakSet : RemoteValue + { + public Handle? Handle { get; set; } -public record GeneratorRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public InternalId? InternalId { get; set; } + } - public InternalId? InternalId { get; set; } -} + public record Generator : RemoteValue + { + public Handle? Handle { get; set; } -public record ErrorRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public InternalId? InternalId { get; set; } + } - public InternalId? InternalId { get; set; } -} + public record Error : RemoteValue + { + public Handle? Handle { get; set; } -public record ProxyRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public InternalId? InternalId { get; set; } + } - public InternalId? InternalId { get; set; } -} + public record Proxy : RemoteValue + { + public Handle? Handle { get; set; } -public record PromiseRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public InternalId? InternalId { get; set; } + } - public InternalId? InternalId { get; set; } -} + public record Promise : RemoteValue + { + public Handle? Handle { get; set; } -public record TypedArrayRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public InternalId? InternalId { get; set; } + } - public InternalId? InternalId { get; set; } -} + public record TypedArray : RemoteValue + { + public Handle? Handle { get; set; } -public record ArrayBufferRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public InternalId? InternalId { get; set; } + } - public InternalId? InternalId { get; set; } -} + public record ArrayBuffer : RemoteValue + { + public Handle? Handle { get; set; } -public record NodeListRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public InternalId? InternalId { get; set; } + } - public InternalId? InternalId { get; set; } + public record NodeList : RemoteValue + { + public Handle? Handle { get; set; } - public IReadOnlyList? Value { get; set; } -} + public InternalId? InternalId { get; set; } -public record HtmlCollectionRemoteValue : RemoteValue -{ - public Handle? Handle { get; set; } + public IReadOnlyList? Value { get; set; } + } - public InternalId? InternalId { get; set; } + public record HtmlCollection : RemoteValue + { + public Handle? Handle { get; set; } - public IReadOnlyList? Value { get; set; } -} + public InternalId? InternalId { get; set; } -public record NodeRemoteValue : RemoteValue -{ - [JsonInclude] - public string? SharedId { get; internal set; } + public IReadOnlyList? Value { get; set; } + } - public Handle? Handle { get; set; } + public record Node : RemoteValue + { + [JsonInclude] + public string? SharedId { get; internal set; } - public InternalId? InternalId { get; set; } + public Handle? Handle { get; set; } - [JsonInclude] - public NodeProperties? Value { get; internal set; } -} + public InternalId? InternalId { get; set; } -public record WindowProxyRemoteValue(WindowProxyProperties Value) : RemoteValue -{ - public Handle? Handle { get; set; } + [JsonInclude] + public NodeProperties? Value { get; internal set; } + } - public InternalId? InternalId { get; set; } + public record WindowProxy(WindowProxy.Properties Value) : RemoteValue + { + public Handle? Handle { get; set; } + + public InternalId? InternalId { get; set; } + + public record Properties(BrowsingContext.BrowsingContext Context); + } } -public record WindowProxyProperties(BrowsingContext.BrowsingContext Context); +public abstract record PrimitiveProtocolRemoteValue : RemoteValue; public enum Mode { diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/RemovePreloadScriptCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Script/RemovePreloadScriptCommand.cs index a64d5dd42775c..850ab9ecc0f6c 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/RemovePreloadScriptCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/RemovePreloadScriptCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; internal class RemovePreloadScriptCommand(RemovePreloadScriptCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/ResultOwnership.cs b/dotnet/src/webdriver/BiDi/Modules/Script/ResultOwnership.cs index 68e77e72a1f01..c69595bc3293c 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/ResultOwnership.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/ResultOwnership.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public enum ResultOwnership diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/ScriptEvaluateException.cs b/dotnet/src/webdriver/BiDi/Modules/Script/ScriptEvaluateException.cs index 2270835f32639..4daa2cc704b1c 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/ScriptEvaluateException.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/ScriptEvaluateException.cs @@ -1,10 +1,12 @@ using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; -public class ScriptEvaluateException(EvaluateResultException evaluateResultException) : Exception +public class ScriptEvaluateException(EvaluateResult.Exception evaluateResultException) : Exception { - private readonly EvaluateResultException _evaluateResultException = evaluateResultException; + private readonly EvaluateResult.Exception _evaluateResultException = evaluateResultException; public string Text => _evaluateResultException.ExceptionDetails.Text; diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/ScriptModule.cs b/dotnet/src/webdriver/BiDi/Modules/Script/ScriptModule.cs index b5e8f2ae61b22..fee6e4a10dcfc 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/ScriptModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/ScriptModule.cs @@ -1,13 +1,16 @@ using OpenQA.Selenium.BiDi.Communication; +using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public sealed class ScriptModule(Broker broker) : Module(broker) { - public async Task EvaluateAsync(string expression, bool awaitPromise, Target target, EvaluateOptions? options = null) + public async Task EvaluateAsync(string expression, bool awaitPromise, Target target, EvaluateOptions? options = null) { var @params = new EvaluateCommandParameters(expression, target, awaitPromise); @@ -20,15 +23,22 @@ public async Task EvaluateAsync(string expression, bool awaitPromis var result = await Broker.ExecuteCommandAsync(new EvaluateCommand(@params), options).ConfigureAwait(false); - if (result is EvaluateResultException exp) + if (result is EvaluateResult.Exception exp) { throw new ScriptEvaluateException(exp); } - return ((EvaluateResultSuccess)result).Result; + return (EvaluateResult.Success)result; } - public async Task CallFunctionAsync(string functionDeclaration, bool awaitPromise, Target target, CallFunctionOptions? options = null) + public async Task EvaluateAsync(string expression, bool awaitPromise, Target target, EvaluateOptions? options = null) + { + var result = await EvaluateAsync(expression, awaitPromise, target, options).ConfigureAwait(false); + + return result.Result.ConvertTo(); + } + + public async Task CallFunctionAsync(string functionDeclaration, bool awaitPromise, Target target, CallFunctionOptions? options = null) { var @params = new CallFunctionCommandParameters(functionDeclaration, awaitPromise, target); @@ -43,15 +53,22 @@ public async Task CallFunctionAsync(string functionDeclaration, boo var result = await Broker.ExecuteCommandAsync(new CallFunctionCommand(@params), options).ConfigureAwait(false); - if (result is EvaluateResultException exp) + if (result is EvaluateResult.Exception exp) { throw new ScriptEvaluateException(exp); } - return ((EvaluateResultSuccess)result).Result; + return (EvaluateResult.Success)result; + } + + public async Task CallFunctionAsync(string functionDeclaration, bool awaitPromise, Target target, CallFunctionOptions? options = null) + { + var result = await CallFunctionAsync(functionDeclaration, awaitPromise, target, options).ConfigureAwait(false); + + return result.Result.ConvertTo(); } - public async Task> GetRealmsAsync(GetRealmsOptions? options = null) + public async Task GetRealmsAsync(GetRealmsOptions? options = null) { var @params = new GetRealmsCommandParameters(); @@ -61,9 +78,7 @@ public async Task> GetRealmsAsync(GetRealmsOptions? opt @params.Type = options.Type; } - var result = await Broker.ExecuteCommandAsync(new GetRealmsCommand(@params), options).ConfigureAwait(false); - - return result.Realms; + return await Broker.ExecuteCommandAsync(new GetRealmsCommand(@params), options).ConfigureAwait(false); } public async Task AddPreloadScriptAsync(string functionDeclaration, AddPreloadScriptOptions? options = null) @@ -88,4 +103,34 @@ public async Task RemovePreloadScriptAsync(PreloadScript script, RemovePreloadSc await Broker.ExecuteCommandAsync(new RemovePreloadScriptCommand(@params), options).ConfigureAwait(false); } + + public async Task OnMessageAsync(Func handler, SubscriptionOptions? options = null) + { + return await Broker.SubscribeAsync("script.message", handler, options).ConfigureAwait(false); + } + + public async Task OnMessageAsync(Action handler, SubscriptionOptions? options = null) + { + return await Broker.SubscribeAsync("script.message", handler, options).ConfigureAwait(false); + } + + public async Task OnRealmCreatedAsync(Func handler, SubscriptionOptions? options = null) + { + return await Broker.SubscribeAsync("script.realmCreated", handler, options).ConfigureAwait(false); + } + + public async Task OnRealmCreatedAsync(Action handler, SubscriptionOptions? options = null) + { + return await Broker.SubscribeAsync("script.realmCreated", handler, options).ConfigureAwait(false); + } + + public async Task OnRealmDestroyedAsync(Func handler, SubscriptionOptions? options = null) + { + return await Broker.SubscribeAsync("script.realmDestroyed", handler, options).ConfigureAwait(false); + } + + public async Task OnRealmDestroyedAsync(Action handler, SubscriptionOptions? options = null) + { + return await Broker.SubscribeAsync("script.realmDestroyed", handler, options).ConfigureAwait(false); + } } diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/SerializationOptions.cs b/dotnet/src/webdriver/BiDi/Modules/Script/SerializationOptions.cs index 57659570eaaf4..bbb275cc08c2f 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/SerializationOptions.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/SerializationOptions.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public class SerializationOptions diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/Source.cs b/dotnet/src/webdriver/BiDi/Modules/Script/Source.cs index 309ac31a0ba75..27bdbcad6f695 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/Source.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/Source.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public record Source(Realm Realm) diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/StackFrame.cs b/dotnet/src/webdriver/BiDi/Modules/Script/StackFrame.cs index 7e3694e9a5e68..208485e0bd636 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/StackFrame.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/StackFrame.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public record StackFrame(long LineNumber, long ColumnNumber, string Url, string FunctionName); diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/StackTrace.cs b/dotnet/src/webdriver/BiDi/Modules/Script/StackTrace.cs index b9ec5c6bf1d0a..15bbfd829bb1e 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/StackTrace.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/StackTrace.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Script; public record StackTrace(IReadOnlyCollection CallFrames); diff --git a/dotnet/src/webdriver/BiDi/Modules/Script/Target.cs b/dotnet/src/webdriver/BiDi/Modules/Script/Target.cs index 7c20cb8b33b6f..a05849a62c8b9 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Script/Target.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Script/Target.cs @@ -1,16 +1,19 @@ using System.Text.Json.Serialization; -namespace OpenQA.Selenium.BiDi.Modules.Script; - -[JsonDerivedType(typeof(RealmTarget))] -[JsonDerivedType(typeof(ContextTarget))] -public abstract record Target; +#nullable enable -public record RealmTarget(Realm Realm) : Target; +namespace OpenQA.Selenium.BiDi.Modules.Script; -public record ContextTarget(BrowsingContext.BrowsingContext Context) : Target +[JsonDerivedType(typeof(Realm))] +[JsonDerivedType(typeof(Context))] +public abstract record Target { - public string? Sandbox { get; set; } + public record Realm([property: JsonPropertyName("realm")] Script.Realm Target) : Target; + + public record Context([property: JsonPropertyName("context")] BrowsingContext.BrowsingContext Target) : Target + { + public string? Sandbox { get; set; } + } } public class ContextTargetOptions diff --git a/dotnet/src/webdriver/BiDi/Modules/Session/CapabilitiesRequest.cs b/dotnet/src/webdriver/BiDi/Modules/Session/CapabilitiesRequest.cs index be4c7ac6d69db..542f9e1c3f076 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Session/CapabilitiesRequest.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Session/CapabilitiesRequest.cs @@ -1,5 +1,7 @@ using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Session; public class CapabilitiesRequest diff --git a/dotnet/src/webdriver/BiDi/Modules/Session/CapabilityRequest.cs b/dotnet/src/webdriver/BiDi/Modules/Session/CapabilityRequest.cs index 1cd33a08be0ad..b449e984bb540 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Session/CapabilityRequest.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Session/CapabilityRequest.cs @@ -1,3 +1,5 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Session; public class CapabilityRequest diff --git a/dotnet/src/webdriver/BiDi/Modules/Session/EndCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Session/EndCommand.cs index 73180d2cd6bda..a41d0be7c2ded 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Session/EndCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Session/EndCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Session; internal class EndCommand() : Command(CommandParameters.Empty); diff --git a/dotnet/src/webdriver/BiDi/Modules/Session/NewCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Session/NewCommand.cs index 3c965e0f1d505..97759165116a2 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Session/NewCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Session/NewCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Session; internal class NewCommand(NewCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Session/ProxyConfiguration.cs b/dotnet/src/webdriver/BiDi/Modules/Session/ProxyConfiguration.cs index 171dbb0ec8c2b..4243dbbaf69c1 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Session/ProxyConfiguration.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Session/ProxyConfiguration.cs @@ -1,32 +1,35 @@ using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Session; [JsonPolymorphic(TypeDiscriminatorPropertyName = "proxyType")] -[JsonDerivedType(typeof(AutodetectProxyConfiguration), "autodetect")] -[JsonDerivedType(typeof(DirectProxyConfiguration), "direct")] -[JsonDerivedType(typeof(ManualProxyConfiguration), "manual")] -[JsonDerivedType(typeof(PacProxyConfiguration), "pac")] -[JsonDerivedType(typeof(SystemProxyConfiguration), "system")] -public abstract record ProxyConfiguration; - -public record AutodetectProxyConfiguration : ProxyConfiguration; +[JsonDerivedType(typeof(Autodetect), "autodetect")] +[JsonDerivedType(typeof(Direct), "direct")] +[JsonDerivedType(typeof(Manual), "manual")] +[JsonDerivedType(typeof(Pac), "pac")] +[JsonDerivedType(typeof(System), "system")] +public abstract record ProxyConfiguration +{ + public record Autodetect : ProxyConfiguration; -public record DirectProxyConfiguration : ProxyConfiguration; + public record Direct : ProxyConfiguration; -public record ManualProxyConfiguration : ProxyConfiguration -{ - public string? FtpProxy { get; set; } + public record Manual : ProxyConfiguration + { + public string? FtpProxy { get; set; } - public string? HttpProxy { get; set; } + public string? HttpProxy { get; set; } - public string? SslProxy { get; set; } + public string? SslProxy { get; set; } - public string? SocksProxy { get; set; } + public string? SocksProxy { get; set; } - public long? SocksVersion { get; set; } -} + public long? SocksVersion { get; set; } + } -public record PacProxyConfiguration(string ProxyAutoconfigUrl) : ProxyConfiguration; + public record Pac(string ProxyAutoconfigUrl) : ProxyConfiguration; -public record SystemProxyConfiguration : ProxyConfiguration; + public record System : ProxyConfiguration; +} diff --git a/dotnet/src/webdriver/BiDi/Modules/Session/SessionModule.cs b/dotnet/src/webdriver/BiDi/Modules/Session/SessionModule.cs index 3a1ec251a2956..88503bf894cd8 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Session/SessionModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Session/SessionModule.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Session; internal sealed class SessionModule(Broker broker) : Module(broker) diff --git a/dotnet/src/webdriver/BiDi/Modules/Session/StatusCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Session/StatusCommand.cs index 2c25c17e60313..8ab6de213b138 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Session/StatusCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Session/StatusCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Session; internal class StatusCommand() : Command(CommandParameters.Empty); diff --git a/dotnet/src/webdriver/BiDi/Modules/Session/SubscribeCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Session/SubscribeCommand.cs index 1d33180d1e3b7..607e5c69944c6 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Session/SubscribeCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Session/SubscribeCommand.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Communication; using System.Collections.Generic; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Session; internal class SubscribeCommand(SubscribeCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Session/UnsubscribeCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Session/UnsubscribeCommand.cs index e96f6b4cf828d..6ca1f7517b439 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Session/UnsubscribeCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Session/UnsubscribeCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Session; internal class UnsubscribeCommand(SubscribeCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Storage/DeleteCookiesCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Storage/DeleteCookiesCommand.cs index bcc27fdf9f8b3..13dcf2f30d3ba 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Storage/DeleteCookiesCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Storage/DeleteCookiesCommand.cs @@ -1,5 +1,7 @@ using OpenQA.Selenium.BiDi.Communication; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Storage; internal class DeleteCookiesCommand(DeleteCookiesCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Storage/GetCookiesCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Storage/GetCookiesCommand.cs index c7c7c2c01dda3..06cd0c09f6925 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Storage/GetCookiesCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Storage/GetCookiesCommand.cs @@ -1,8 +1,11 @@ using OpenQA.Selenium.BiDi.Communication; using System; +using System.Collections; using System.Collections.Generic; using System.Text.Json.Serialization; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Storage; internal class GetCookiesCommand(GetCookiesCommandParameters @params) : Command(@params); @@ -21,7 +24,26 @@ public record GetCookiesOptions : CommandOptions public PartitionDescriptor? Partition { get; set; } } -public record GetCookiesResult(IReadOnlyList Cookies, PartitionKey PartitionKey); +public record GetCookiesResult : IReadOnlyList +{ + private readonly IReadOnlyList _cookies; + + internal GetCookiesResult(IReadOnlyList cookies, PartitionKey partitionKey) + { + _cookies = cookies; + PartitionKey = partitionKey; + } + + public PartitionKey PartitionKey { get; init; } + + public Network.Cookie this[int index] => _cookies[index]; + + public int Count => _cookies.Count; + + public IEnumerator GetEnumerator() => _cookies.GetEnumerator(); + + IEnumerator IEnumerable.GetEnumerator() => (_cookies as IEnumerable).GetEnumerator(); +} public class CookieFilter { @@ -45,15 +67,16 @@ public class CookieFilter } [JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] -[JsonDerivedType(typeof(BrowsingContextPartitionDescriptor), "context")] -[JsonDerivedType(typeof(StorageKeyPartitionDescriptor), "storageKey")] -public abstract record PartitionDescriptor; - -public record BrowsingContextPartitionDescriptor(BrowsingContext.BrowsingContext Context) : PartitionDescriptor; - -public record StorageKeyPartitionDescriptor : PartitionDescriptor +[JsonDerivedType(typeof(Context), "context")] +[JsonDerivedType(typeof(StorageKey), "storageKey")] +public abstract record PartitionDescriptor { - public string? UserContext { get; set; } + public record Context([property: JsonPropertyName("context")] BrowsingContext.BrowsingContext Descriptor) : PartitionDescriptor; + + public record StorageKey : PartitionDescriptor + { + public string? UserContext { get; set; } - public string? SourceOrigin { get; set; } + public string? SourceOrigin { get; set; } + } } diff --git a/dotnet/src/webdriver/BiDi/Modules/Storage/PartitionKey.cs b/dotnet/src/webdriver/BiDi/Modules/Storage/PartitionKey.cs index 2d9ac37a41a22..30200db4b5f60 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Storage/PartitionKey.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Storage/PartitionKey.cs @@ -1,8 +1,10 @@ +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Storage; public class PartitionKey { - public string? UserContext { get; set; } + public Browser.UserContext? UserContext { get; set; } public string? SourceOrigin { get; set; } } diff --git a/dotnet/src/webdriver/BiDi/Modules/Storage/SetCookieCommand.cs b/dotnet/src/webdriver/BiDi/Modules/Storage/SetCookieCommand.cs index f0dcf22904eba..7e7cb79824cbc 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Storage/SetCookieCommand.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Storage/SetCookieCommand.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Communication; using System; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Storage; internal class SetCookieCommand(SetCookieCommandParameters @params) : Command(@params); diff --git a/dotnet/src/webdriver/BiDi/Modules/Storage/StorageModule.cs b/dotnet/src/webdriver/BiDi/Modules/Storage/StorageModule.cs index 249d7d37634d1..71969d9914c2a 100644 --- a/dotnet/src/webdriver/BiDi/Modules/Storage/StorageModule.cs +++ b/dotnet/src/webdriver/BiDi/Modules/Storage/StorageModule.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Communication; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi.Modules.Storage; public class StorageModule(Broker broker) : Module(broker) diff --git a/dotnet/src/webdriver/BiDi/Subscription.cs b/dotnet/src/webdriver/BiDi/Subscription.cs index 21995fee00dcf..8870b07fb18bf 100644 --- a/dotnet/src/webdriver/BiDi/Subscription.cs +++ b/dotnet/src/webdriver/BiDi/Subscription.cs @@ -3,22 +3,24 @@ using System.Collections.Generic; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi; public class Subscription : IAsyncDisposable { - private readonly Broker Broker; + private readonly Broker _broker; private readonly Communication.EventHandler _eventHandler; internal Subscription(Broker broker, Communication.EventHandler eventHandler) { - Broker = broker; + _broker = broker; _eventHandler = eventHandler; } public async Task UnsubscribeAsync() { - await Broker.UnsubscribeAsync(_eventHandler).ConfigureAwait(false); + await _broker.UnsubscribeAsync(_eventHandler).ConfigureAwait(false); } public async ValueTask DisposeAsync() diff --git a/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs b/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs index c039dd4087740..a3e7df31cec50 100644 --- a/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs +++ b/dotnet/src/webdriver/BiDi/WebDriver.Extensions.cs @@ -1,6 +1,8 @@ using OpenQA.Selenium.BiDi.Modules.BrowsingContext; using System.Threading.Tasks; +#nullable enable + namespace OpenQA.Selenium.BiDi; public static class WebDriverExtensions diff --git a/dotnet/src/webdriver/Command.cs b/dotnet/src/webdriver/Command.cs index d12274ca90d7a..e6d7c422cbc1e 100644 --- a/dotnet/src/webdriver/Command.cs +++ b/dotnet/src/webdriver/Command.cs @@ -28,15 +28,16 @@ namespace OpenQA.Selenium /// public class Command { + private SessionId commandSessionId; + private string commandName; + private Dictionary commandParameters = new Dictionary(); + private readonly static JsonSerializerOptions s_jsonSerializerOptions = new() { + TypeInfoResolver = CommandJsonSerializerContext.Default, Converters = { new ResponseValueJsonConverter() } }; - private SessionId commandSessionId; - private string commandName; - private Dictionary commandParameters = new Dictionary(); - /// /// Initializes a new instance of the class using a command name and a JSON-encoded string for the parameters. /// @@ -101,7 +102,7 @@ public string ParametersAsJsonString string parametersString = string.Empty; if (this.commandParameters != null && this.commandParameters.Count > 0) { - parametersString = JsonSerializer.Serialize(this.commandParameters); + parametersString = JsonSerializer.Serialize(this.commandParameters, s_jsonSerializerOptions); } if (string.IsNullOrEmpty(parametersString)) @@ -133,4 +134,35 @@ private static Dictionary ConvertParametersFromJson(string value return parameters; } } + + // Built-in types + [JsonSerializable(typeof(bool))] + [JsonSerializable(typeof(byte))] + [JsonSerializable(typeof(sbyte))] + [JsonSerializable(typeof(char))] + [JsonSerializable(typeof(decimal))] + [JsonSerializable(typeof(double))] + [JsonSerializable(typeof(float))] + [JsonSerializable(typeof(int))] + [JsonSerializable(typeof(uint))] + [JsonSerializable(typeof(nint))] + [JsonSerializable(typeof(nuint))] + [JsonSerializable(typeof(long))] + [JsonSerializable(typeof(ulong))] + [JsonSerializable(typeof(short))] + [JsonSerializable(typeof(ushort))] + + [JsonSerializable(typeof(string))] + + // Selenium WebDriver types + [JsonSerializable(typeof(char[]))] + [JsonSerializable(typeof(byte[]))] + [JsonSerializable(typeof(Dictionary))] + [JsonSerializable(typeof(Cookie))] + [JsonSerializable(typeof(ReturnedCookie))] + [JsonSerializable(typeof(Proxy))] + internal partial class CommandJsonSerializerContext : JsonSerializerContext + { + + } } diff --git a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs index f29e5eecaaf09..633997c63460a 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsDomains.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsDomains.cs @@ -35,7 +35,7 @@ public abstract class DevToolsDomains // added to this dictionary. private static readonly Dictionary SupportedDevToolsVersions = new Dictionary() { - { 127, typeof(V127.V127Domains) }, + { 130, typeof(V130.V130Domains) }, { 129, typeof(V129.V129Domains) }, { 128, typeof(V128.V128Domains) }, { 85, typeof(V85.V85Domains) } diff --git a/dotnet/src/webdriver/DevTools/v127/V127Domains.cs b/dotnet/src/webdriver/DevTools/v130/V130Domains.cs similarity index 78% rename from dotnet/src/webdriver/DevTools/v127/V127Domains.cs rename to dotnet/src/webdriver/DevTools/v130/V130Domains.cs index 80f7ba6be7e34..e25e338268203 100644 --- a/dotnet/src/webdriver/DevTools/v127/V127Domains.cs +++ b/dotnet/src/webdriver/DevTools/v130/V130Domains.cs @@ -1,4 +1,4 @@ -// +// // Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information @@ -15,20 +15,20 @@ // See the License for the specific language governing permissions and // limitations under the License. -namespace OpenQA.Selenium.DevTools.V127 +namespace OpenQA.Selenium.DevTools.V130 { /// - /// Class containing the domain implementation for version 127 of the DevTools Protocol. + /// Class containing the domain implementation for version 130 of the DevTools Protocol. /// - public class V127Domains : DevToolsDomains + public class V130Domains : DevToolsDomains { private DevToolsSessionDomains domains; /// - /// Initializes a new instance of the V127Domains class. + /// Initializes a new instance of the V130Domains class. /// /// The DevToolsSession to use with this set of domains. - public V127Domains(DevToolsSession session) + public V130Domains(DevToolsSession session) { this.domains = new DevToolsSessionDomains(session); } @@ -36,7 +36,7 @@ public V127Domains(DevToolsSession session) /// /// Gets the DevTools Protocol version for which this class is valid. /// - public static int DevToolsVersion => 127; + public static int DevToolsVersion => 130; /// /// Gets the version-specific domains for the DevTools session. This value must be cast to a version specific type to be at all useful. @@ -46,21 +46,21 @@ public V127Domains(DevToolsSession session) /// /// Gets the object used for manipulating network information in the browser. /// - public override DevTools.Network Network => new V127Network(domains.Network, domains.Fetch); + public override DevTools.Network Network => new V130Network(domains.Network, domains.Fetch); /// /// Gets the object used for manipulating the browser's JavaScript execution. /// - public override JavaScript JavaScript => new V127JavaScript(domains.Runtime, domains.Page); + public override JavaScript JavaScript => new V130JavaScript(domains.Runtime, domains.Page); /// /// Gets the object used for manipulating DevTools Protocol targets. /// - public override DevTools.Target Target => new V127Target(domains.Target); + public override DevTools.Target Target => new V130Target(domains.Target); /// /// Gets the object used for manipulating the browser's logs. /// - public override DevTools.Log Log => new V127Log(domains.Log); + public override DevTools.Log Log => new V130Log(domains.Log); } } diff --git a/dotnet/src/webdriver/DevTools/v127/V127JavaScript.cs b/dotnet/src/webdriver/DevTools/v130/V130JavaScript.cs similarity index 94% rename from dotnet/src/webdriver/DevTools/v127/V127JavaScript.cs rename to dotnet/src/webdriver/DevTools/v130/V130JavaScript.cs index 7476ad37f2362..74a508ae60520 100644 --- a/dotnet/src/webdriver/DevTools/v127/V127JavaScript.cs +++ b/dotnet/src/webdriver/DevTools/v130/V130JavaScript.cs @@ -1,4 +1,4 @@ -// +// // Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information @@ -15,28 +15,28 @@ // See the License for the specific language governing permissions and // limitations under the License. -using OpenQA.Selenium.DevTools.V127.Page; -using OpenQA.Selenium.DevTools.V127.Runtime; +using OpenQA.Selenium.DevTools.V130.Page; +using OpenQA.Selenium.DevTools.V130.Runtime; using System; using System.Collections.Generic; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V127 +namespace OpenQA.Selenium.DevTools.V130 { /// - /// Class containing the JavaScript implementation for version 127 of the DevTools Protocol. + /// Class containing the JavaScript implementation for version 130 of the DevTools Protocol. /// - public class V127JavaScript : JavaScript + public class V130JavaScript : JavaScript { private RuntimeAdapter runtime; private PageAdapter page; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The DevTools Protocol adapter for the Runtime domain. /// The DevTools Protocol adapter for the Page domain. - public V127JavaScript(RuntimeAdapter runtime, PageAdapter page) + public V130JavaScript(RuntimeAdapter runtime, PageAdapter page) { this.runtime = runtime; this.page = page; diff --git a/dotnet/src/webdriver/DevTools/v127/V127Log.cs b/dotnet/src/webdriver/DevTools/v130/V130Log.cs similarity index 88% rename from dotnet/src/webdriver/DevTools/v127/V127Log.cs rename to dotnet/src/webdriver/DevTools/v130/V130Log.cs index 991b205db4e42..cc8d62e546da3 100644 --- a/dotnet/src/webdriver/DevTools/v127/V127Log.cs +++ b/dotnet/src/webdriver/DevTools/v130/V130Log.cs @@ -1,4 +1,4 @@ -// +// // Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information @@ -15,23 +15,23 @@ // See the License for the specific language governing permissions and // limitations under the License. -using OpenQA.Selenium.DevTools.V127.Log; +using OpenQA.Selenium.DevTools.V130.Log; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V127 +namespace OpenQA.Selenium.DevTools.V130 { /// - /// Class containing the browser's log as referenced by version 127 of the DevTools Protocol. + /// Class containing the browser's log as referenced by version 130 of the DevTools Protocol. /// - public class V127Log : DevTools.Log + public class V130Log : DevTools.Log { private LogAdapter adapter; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The adapter for the Log domain. - public V127Log(LogAdapter adapter) + public V130Log(LogAdapter adapter) { this.adapter = adapter; this.adapter.EntryAdded += OnAdapterEntryAdded; diff --git a/dotnet/src/webdriver/DevTools/v127/V127Network.cs b/dotnet/src/webdriver/DevTools/v130/V130Network.cs similarity index 95% rename from dotnet/src/webdriver/DevTools/v127/V127Network.cs rename to dotnet/src/webdriver/DevTools/v130/V130Network.cs index b31fa466d7efd..1d6554c315a0e 100644 --- a/dotnet/src/webdriver/DevTools/v127/V127Network.cs +++ b/dotnet/src/webdriver/DevTools/v130/V130Network.cs @@ -1,4 +1,4 @@ -// +// // Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information @@ -16,29 +16,29 @@ // limitations under the License. // -using OpenQA.Selenium.DevTools.V127.Fetch; -using OpenQA.Selenium.DevTools.V127.Network; +using OpenQA.Selenium.DevTools.V130.Fetch; +using OpenQA.Selenium.DevTools.V130.Network; using System; using System.Collections.Generic; using System.Text; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V127 +namespace OpenQA.Selenium.DevTools.V130 { /// - /// Class providing functionality for manipulating network calls using version 127 of the DevTools Protocol + /// Class providing functionality for manipulating network calls using version 130 of the DevTools Protocol /// - public class V127Network : DevTools.Network + public class V130Network : DevTools.Network { private FetchAdapter fetch; private NetworkAdapter network; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The adapter for the Network domain. /// The adapter for the Fetch domain. - public V127Network(NetworkAdapter network, FetchAdapter fetch) + public V130Network(NetworkAdapter network, FetchAdapter fetch) { this.network = network; this.fetch = fetch; @@ -216,9 +216,9 @@ public override async Task ContinueWithAuth(string requestId, string userName, s await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() { RequestId = requestId, - AuthChallengeResponse = new V127.Fetch.AuthChallengeResponse() + AuthChallengeResponse = new V130.Fetch.AuthChallengeResponse() { - Response = V127.Fetch.AuthChallengeResponseResponseValues.ProvideCredentials, + Response = V130.Fetch.AuthChallengeResponseResponseValues.ProvideCredentials, Username = userName, Password = password } @@ -235,9 +235,9 @@ public override async Task CancelAuth(string requestId) await fetch.ContinueWithAuth(new ContinueWithAuthCommandSettings() { RequestId = requestId, - AuthChallengeResponse = new OpenQA.Selenium.DevTools.V127.Fetch.AuthChallengeResponse() + AuthChallengeResponse = new OpenQA.Selenium.DevTools.V130.Fetch.AuthChallengeResponse() { - Response = V127.Fetch.AuthChallengeResponseResponseValues.CancelAuth + Response = V130.Fetch.AuthChallengeResponseResponseValues.CancelAuth } }).ConfigureAwait(false); } diff --git a/dotnet/src/webdriver/DevTools/v127/V127Target.cs b/dotnet/src/webdriver/DevTools/v130/V130Target.cs similarity index 94% rename from dotnet/src/webdriver/DevTools/v127/V127Target.cs rename to dotnet/src/webdriver/DevTools/v130/V130Target.cs index 977b569b276b1..99e4d87729663 100644 --- a/dotnet/src/webdriver/DevTools/v127/V127Target.cs +++ b/dotnet/src/webdriver/DevTools/v130/V130Target.cs @@ -1,4 +1,4 @@ -// +// // Licensed to the Software Freedom Conservancy (SFC) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information @@ -16,26 +16,26 @@ // limitations under the License. // -using OpenQA.Selenium.DevTools.V127.Target; +using OpenQA.Selenium.DevTools.V130.Target; using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Threading.Tasks; -namespace OpenQA.Selenium.DevTools.V127 +namespace OpenQA.Selenium.DevTools.V130 { /// - /// Class providing functionality for manipulating targets for version 127 of the DevTools Protocol + /// Class providing functionality for manipulating targets for version 130 of the DevTools Protocol /// - public class V127Target : DevTools.Target + public class V130Target : DevTools.Target { private TargetAdapter adapter; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The adapter for the Target domain. - public V127Target(TargetAdapter adapter) + public V130Target(TargetAdapter adapter) { this.adapter = adapter; adapter.DetachedFromTarget += OnDetachedFromTarget; diff --git a/dotnet/src/webdriver/IWebElement.cs b/dotnet/src/webdriver/IWebElement.cs index 89261bdd88435..ffe56565fe8f1 100644 --- a/dotnet/src/webdriver/IWebElement.cs +++ b/dotnet/src/webdriver/IWebElement.cs @@ -16,6 +16,7 @@ // limitations under the License. // +using System; using System.Drawing; namespace OpenQA.Selenium @@ -170,6 +171,7 @@ public interface IWebElement : ISearchContext /// /// /// Thrown when the target element is no longer valid in the document DOM. + [Obsolete("Use GetDomAttribute(string attributeName) or GetDomProperty(string propertyName). GetAttribute(string attributeName) will be removed in Selenium 6.")] string GetAttribute(string attributeName); /// diff --git a/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs b/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs index 9967d790c55c9..6b888a788236b 100644 --- a/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs +++ b/dotnet/src/webdriver/Internal/ResponseValueJsonConverter.cs @@ -30,75 +30,107 @@ internal class ResponseValueJsonConverter : JsonConverter { public override object Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - return this.ProcessToken(ref reader, options); + return ProcessReadToken(ref reader, options); } public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options) { - JsonSerializer.Serialize(writer, value, options); + switch (value) + { + case null: + writer.WriteNullValue(); + break; + case Enum: + writer.WriteNumberValue(Convert.ToInt64(value)); + break; + case IEnumerable list: + writer.WriteStartArray(); + foreach (var item in list) + { + Write(writer, item, options); + } + writer.WriteEndArray(); + break; + case IDictionary dictionary: + writer.WriteStartObject(); + foreach (var pair in dictionary) + { + writer.WritePropertyName(pair.Key); + Write(writer, pair.Value, options); + } + writer.WriteEndObject(); + break; + case object obj: + JsonSerializer.Serialize(writer, obj, options.GetTypeInfo(obj.GetType())); + break; + } } - private object ProcessToken(ref Utf8JsonReader reader, JsonSerializerOptions options) + private static object ProcessReadToken(ref Utf8JsonReader reader, JsonSerializerOptions options) { // Recursively processes a token. This is required for elements that next other elements. - object processedObject = null; + object processedObject; - if (reader.TokenType == JsonTokenType.StartObject) + switch (reader.TokenType) { - Dictionary dictionaryValue = new Dictionary(); - while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) - { - string elementKey = reader.GetString(); - reader.Read(); - dictionaryValue.Add(elementKey, this.ProcessToken(ref reader, options)); - } + case JsonTokenType.StartObject: + { + Dictionary dictionaryValue = []; + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) + { + string elementKey = reader.GetString(); + reader.Read(); + dictionaryValue.Add(elementKey, ProcessReadToken(ref reader, options)); + } - processedObject = dictionaryValue; - } - else if (reader.TokenType == JsonTokenType.StartArray) - { - List arrayValue = new List(); - while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) - { - arrayValue.Add(this.ProcessToken(ref reader, options)); - } + processedObject = dictionaryValue; + break; + } - processedObject = arrayValue.ToArray(); - } - else if (reader.TokenType == JsonTokenType.Null) - { - processedObject = null; - } - else if (reader.TokenType == JsonTokenType.False) - { - processedObject = false; - } - else if (reader.TokenType == JsonTokenType.True) - { - processedObject = true; - } - else if (reader.TokenType == JsonTokenType.String) - { - processedObject = reader.GetString(); - } - else if (reader.TokenType == JsonTokenType.Number) - { - if (reader.TryGetInt64(out long longValue)) - { - processedObject = longValue; - } - else if (reader.TryGetDouble(out double doubleValue)) - { - processedObject = doubleValue; - } - else - { - throw new JsonException($"Unrecognized '{JsonElement.ParseValue(ref reader)}' token as a number value."); - } - } - else - { - throw new JsonException($"Unrecognized '{reader.TokenType}' token type while parsing command response."); + case JsonTokenType.StartArray: + { + List arrayValue = []; + while (reader.Read() && reader.TokenType != JsonTokenType.EndArray) + { + arrayValue.Add(ProcessReadToken(ref reader, options)); + } + + processedObject = arrayValue.ToArray(); + break; + } + + case JsonTokenType.Null: + processedObject = null; + break; + case JsonTokenType.False: + processedObject = false; + break; + case JsonTokenType.True: + processedObject = true; + break; + case JsonTokenType.String: + processedObject = reader.GetString(); + break; + case JsonTokenType.Number: + { + if (reader.TryGetInt64(out long longValue)) + { + processedObject = longValue; + } + else if (reader.TryGetDouble(out double doubleValue)) + { + processedObject = doubleValue; + } + else + { + throw new JsonException($"Unrecognized '{JsonElement.ParseValue(ref reader)}' token as a number value."); + } + + break; + } + + default: + throw new JsonException($"Unrecognized '{reader.TokenType}' token type while parsing command response."); } return processedObject; diff --git a/dotnet/src/webdriver/Response.cs b/dotnet/src/webdriver/Response.cs index 674a6488135b4..ebfd47e2cfa1b 100644 --- a/dotnet/src/webdriver/Response.cs +++ b/dotnet/src/webdriver/Response.cs @@ -21,6 +21,7 @@ using System.Collections.Generic; using System.Globalization; using System.Text.Json; +using System.Text.Json.Serialization; namespace OpenQA.Selenium { @@ -31,7 +32,8 @@ public class Response { private readonly static JsonSerializerOptions s_jsonSerializerOptions = new() { - Converters = { new ResponseValueJsonConverter() } + TypeInfoResolver = ResponseJsonSerializerContext.Default, + Converters = { new ResponseValueJsonConverter() } // we still need it to make `Object` as `Dictionary` }; private object responseValue; @@ -208,4 +210,10 @@ public override string ToString() return string.Format(CultureInfo.InvariantCulture, "({0} {1}: {2})", this.SessionId, this.Status, this.Value); } } + + [JsonSerializable(typeof(Dictionary))] + internal partial class ResponseJsonSerializerContext : JsonSerializerContext + { + + } } diff --git a/dotnet/src/webdriver/SeleniumManager.cs b/dotnet/src/webdriver/SeleniumManager.cs index 4b28f7862d138..60c8cd5255389 100644 --- a/dotnet/src/webdriver/SeleniumManager.cs +++ b/dotnet/src/webdriver/SeleniumManager.cs @@ -195,28 +195,12 @@ private static ResultResponse RunCommand(string fileName, string arguments) if (jsonResponse.Logs is not null) { - foreach (var entry in jsonResponse.Logs) + // Treat SM's logs always as Trace to avoid SM writing at Info level + if (_logger.IsEnabled(LogEventLevel.Trace)) { - switch (entry.Level) + foreach (var entry in jsonResponse.Logs) { - case "WARN": - if (_logger.IsEnabled(LogEventLevel.Warn)) - { - _logger.Warn(entry.Message); - } - break; - case "DEBUG": - if (_logger.IsEnabled(LogEventLevel.Debug)) - { - _logger.Debug(entry.Message); - } - break; - case "INFO": - if (_logger.IsEnabled(LogEventLevel.Info)) - { - _logger.Info(entry.Message); - } - break; + _logger.Trace($"{entry.Level} {entry.Message}"); } } } diff --git a/dotnet/test/common/BiDi/BiDiFixture.cs b/dotnet/test/common/BiDi/BiDiFixture.cs new file mode 100644 index 0000000000000..b7bf01de81b52 --- /dev/null +++ b/dotnet/test/common/BiDi/BiDiFixture.cs @@ -0,0 +1,54 @@ +using NUnit.Framework; +using OpenQA.Selenium.Environment; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi; + +[Parallelizable(ParallelScope.All)] +[FixtureLifeCycle(LifeCycle.InstancePerTestCase)] +public class BiDiTestFixture +{ + protected IWebDriver driver; + protected BiDi bidi; + protected Modules.BrowsingContext.BrowsingContext context; + + protected UrlBuilder UrlBuilder { get; } = EnvironmentManager.Instance.UrlBuilder; + + [SetUp] + public async Task BiDiSetUp() + { + var options = new BiDiEnabledDriverOptions() + { + UseWebSocketUrl = true, + UnhandledPromptBehavior = UnhandledPromptBehavior.Ignore, + }; + + driver = EnvironmentManager.Instance.CreateDriverInstance(options); + + context = await driver.AsBiDiContextAsync(); + bidi = context.BiDi; + } + + [TearDown] + public async Task BiDiTearDown() + { + if (bidi is not null) + { + await bidi.DisposeAsync(); + } + + driver?.Dispose(); + } + + public class BiDiEnabledDriverOptions : DriverOptions + { + public override void AddAdditionalOption(string capabilityName, object capabilityValue) + { + } + + public override ICapabilities ToCapabilities() + { + return null; + } + } +} diff --git a/dotnet/test/common/BiDi/Browser/BrowserTest.cs b/dotnet/test/common/BiDi/Browser/BrowserTest.cs new file mode 100644 index 0000000000000..74bbd956848a1 --- /dev/null +++ b/dotnet/test/common/BiDi/Browser/BrowserTest.cs @@ -0,0 +1,43 @@ +using NUnit.Framework; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi.Browser; + +class BrowserTest : BiDiTestFixture +{ + [Test] + public async Task CanCreateUserContext() + { + var userContext = await bidi.Browser.CreateUserContextAsync(); + + Assert.That(userContext, Is.Not.Null); + } + + [Test] + public async Task CanGetUserContexts() + { + var userContext1 = await bidi.Browser.CreateUserContextAsync(); + var userContext2 = await bidi.Browser.CreateUserContextAsync(); + + var userContexts = await bidi.Browser.GetUserContextsAsync(); + + Assert.That(userContexts, Is.Not.Null); + Assert.That(userContexts.Count, Is.GreaterThanOrEqualTo(2)); + Assert.That(userContexts, Does.Contain(userContext1)); + Assert.That(userContexts, Does.Contain(userContext2)); + } + + [Test] + public async Task CanRemoveUserContext() + { + var userContext1 = await bidi.Browser.CreateUserContextAsync(); + var userContext2 = await bidi.Browser.CreateUserContextAsync(); + + await userContext2.UserContext.RemoveAsync(); + + var userContexts = await bidi.Browser.GetUserContextsAsync(); + + Assert.That(userContexts, Does.Contain(userContext1)); + Assert.That(userContexts, Does.Not.Contain(userContext2)); + } +} diff --git a/dotnet/test/common/BiDi/BrowsingContext/BrowsingContextTest.cs b/dotnet/test/common/BiDi/BrowsingContext/BrowsingContextTest.cs new file mode 100644 index 0000000000000..510daceddfb11 --- /dev/null +++ b/dotnet/test/common/BiDi/BrowsingContext/BrowsingContextTest.cs @@ -0,0 +1,301 @@ +using NUnit.Framework; +using OpenQA.Selenium.BiDi.Modules.BrowsingContext; +using System.Linq; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi.BrowsingContext; + +class BrowsingContextTest : BiDiTestFixture +{ + [Test] + public async Task CanCreateNewTab() + { + var tab = await bidi.BrowsingContext.CreateAsync(ContextType.Tab); + + Assert.That(tab, Is.Not.Null); + } + + [Test] + public async Task CanCreateNewTabWithReferencedContext() + { + var tab = await bidi.BrowsingContext.CreateAsync(ContextType.Tab, new() + { + ReferenceContext = context + }); + + Assert.That(tab, Is.Not.Null); + } + + [Test] + public async Task CanCreateNewWindow() + { + var window = await bidi.BrowsingContext.CreateAsync(ContextType.Window); + + Assert.That(window, Is.Not.Null); + } + + [Test] + public async Task CanCreateNewWindowWithReferencedContext() + { + var window = await bidi.BrowsingContext.CreateAsync(ContextType.Window, new() + { + ReferenceContext = context + }); + + Assert.That(window, Is.Not.Null); + } + + [Test] + public async Task CanCreateContextWithAllParameters() + { + var userContext = await bidi.Browser.CreateUserContextAsync(); + + var window = await bidi.BrowsingContext.CreateAsync(ContextType.Window, new() + { + ReferenceContext = context, + UserContext = userContext.UserContext, + Background = true + }); + + Assert.That(window, Is.Not.Null); + } + + [Test] + public async Task CanNavigateToUrl() + { + var info = await context.NavigateAsync(UrlBuilder.WhereIs("/bidi/logEntryAdded.html")); + + Assert.That(info.Url, Does.Contain("/bidi/logEntryAdded.html")); + } + + [Test] + public async Task CanNavigateToUrlWithReadinessState() + { + var info = await context.NavigateAsync(UrlBuilder.WhereIs("/bidi/logEntryAdded.html"), new() + { + Wait = ReadinessState.Complete + }); + + Assert.That(info, Is.Not.Null); + Assert.That(info.Url, Does.Contain("/bidi/logEntryAdded.html")); + } + + [Test] + public async Task CanGetTreeWithChild() + { + await context.NavigateAsync(UrlBuilder.WhereIs("iframes.html"), new() { Wait = ReadinessState.Complete }); + + var tree = await context.GetTreeAsync(); + + Assert.That(tree, Has.Count.EqualTo(1)); + Assert.That(tree[0].Context, Is.EqualTo(context)); + Assert.That(tree[0].Children, Has.Count.EqualTo(1)); + Assert.That(tree[0].Children[0].Url, Does.Contain("formPage.html")); + } + + [Test] + public async Task CanGetTreeWithDepth() + { + await context.NavigateAsync(UrlBuilder.WhereIs("iframes.html"), new() { Wait = ReadinessState.Complete }); + + var tree = await context.GetTreeAsync(new() { MaxDepth = 0 }); + + Assert.That(tree, Has.Count.EqualTo(1)); + Assert.That(tree[0].Context, Is.EqualTo(context)); + Assert.That(tree[0].Children, Is.Null); + } + + [Test] + public async Task CanGetTreeTopLevel() + { + var window1 = await bidi.BrowsingContext.CreateAsync(ContextType.Window); + var window2 = await bidi.BrowsingContext.CreateAsync(ContextType.Window); + + var tree = await bidi.BrowsingContext.GetTreeAsync(); + + Assert.That(tree, Has.Count.GreaterThanOrEqualTo(2)); + } + + [Test] + public async Task CanCloseWindow() + { + var window = await bidi.BrowsingContext.CreateAsync(ContextType.Window); + + await window.CloseAsync(); + + var tree = await bidi.BrowsingContext.GetTreeAsync(); + + Assert.That(tree.Select(i => i.Context), Does.Not.Contain(window)); + } + + [Test] + public async Task CanCloseTab() + { + var tab = await bidi.BrowsingContext.CreateAsync(ContextType.Tab); + + await tab.CloseAsync(); + + var tree = await bidi.BrowsingContext.GetTreeAsync(); + + Assert.That(tree.Select(i => i.Context), Does.Not.Contain(tab)); + } + + [Test] + public async Task CanActivate() + { + await context.ActivateAsync(); + + // TODO: Add assertion when https://w3c.github.io/webdriver-bidi/#type-browser-ClientWindowInfo is implemented + } + + [Test] + public async Task CanReload() + { + string url = UrlBuilder.WhereIs("/bidi/logEntryAdded.html"); + + await context.NavigateAsync(url, new() { Wait = ReadinessState.Complete }); + + var info = await context.ReloadAsync(); + + Assert.That(info, Is.Not.Null); + Assert.That(info.Url, Does.Contain("/bidi/logEntryAdded.html")); + } + + [Test] + public async Task CanReloadWithReadinessState() + { + string url = UrlBuilder.WhereIs("/bidi/logEntryAdded.html"); + + await context.NavigateAsync(url, new() { Wait = ReadinessState.Complete }); + + var info = await context.ReloadAsync(new() + { + Wait = ReadinessState.Complete + }); + + Assert.That(info, Is.Not.Null); + Assert.That(info.Url, Does.Contain("/bidi/logEntryAdded.html")); + } + + [Test] + public async Task CanHandleUserPrompt() + { + await context.NavigateAsync(UrlBuilder.WhereIs("alerts.html"), new() { Wait = ReadinessState.Complete }); + + driver.FindElement(By.Id("alert")).Click(); + + await context.HandleUserPromptAsync(); + } + + [Test] + public async Task CanAcceptUserPrompt() + { + await context.NavigateAsync(UrlBuilder.WhereIs("alerts.html"), new() { Wait = ReadinessState.Complete }); + + driver.FindElement(By.Id("alert")).Click(); + + await context.HandleUserPromptAsync(new() + { + Accept = true + }); + } + + [Test] + public async Task CanDismissUserPrompt() + { + await context.NavigateAsync(UrlBuilder.WhereIs("alerts.html"), new() { Wait = ReadinessState.Complete }); + + driver.FindElement(By.Id("alert")).Click(); + + await context.HandleUserPromptAsync(new() + { + Accept = false + }); + } + + [Test] + public async Task CanPassUserTextToPrompt() + { + await context.NavigateAsync(UrlBuilder.WhereIs("alerts.html"), new() { Wait = ReadinessState.Complete }); + + driver.FindElement(By.Id("alert")).Click(); + + await context.HandleUserPromptAsync(new() + { + UserText = "Selenium automates browsers" + }); + } + + [Test] + public async Task CanCaptureScreenshot() + { + var screenshot = await context.CaptureScreenshotAsync(); + + Assert.That(screenshot, Is.Not.Null); + Assert.That(screenshot.Data, Is.Not.Empty); + } + + [Test] + public async Task CanCaptureScreenshotWithParameters() + { + var screenshot = await context.CaptureScreenshotAsync(new() + { + Origin = Origin.Document, + Clip = new ClipRectangle.Box(5, 5, 10, 10) + }); + + Assert.That(screenshot, Is.Not.Null); + Assert.That(screenshot.Data, Is.Not.Empty); + } + + [Test] + public async Task CanCaptureScreenshotOfViewport() + { + var screenshot = await context.CaptureScreenshotAsync(new() + { + Origin = Origin.Viewport, + Clip = new ClipRectangle.Box(5, 5, 10, 10) + }); + + Assert.That(screenshot, Is.Not.Null); + Assert.That(screenshot.Data, Is.Not.Empty); + } + + [Test] + public async Task CanCaptureScreenshotOfElement() + { + await context.NavigateAsync(UrlBuilder.WhereIs("formPage.html"), new() { Wait = ReadinessState.Complete }); + + var nodes = await context.LocateNodesAsync(new Locator.Css("#checky")); + + var screenshot = await context.CaptureScreenshotAsync(new() + { + // TODO: Seems Node implements ISharedReference + Clip = new ClipRectangle.Element(new Modules.Script.SharedReference(nodes[0].SharedId)) + }); + + Assert.That(screenshot, Is.Not.Null); + Assert.That(screenshot.Data, Is.Not.Empty); + } + + [Test] + public async Task CanSetViewport() + { + await context.SetViewportAsync(new() { Viewport = new(250, 300) }); + } + + [Test] + public async Task CanSetViewportWithDevicePixelRatio() + { + await context.SetViewportAsync(new() { Viewport = new(250, 300), DevicePixelRatio = 5 }); + } + + [Test] + public async Task CanPrintPage() + { + var pdf = await context.PrintAsync(); + + Assert.That(pdf, Is.Not.Null); + Assert.That(pdf.Data, Is.Not.Empty); + } +} diff --git a/dotnet/test/common/BiDi/Input/CombinedInputActionsTest.cs b/dotnet/test/common/BiDi/Input/CombinedInputActionsTest.cs new file mode 100644 index 0000000000000..cb832cdeb9e41 --- /dev/null +++ b/dotnet/test/common/BiDi/Input/CombinedInputActionsTest.cs @@ -0,0 +1,55 @@ +using NUnit.Framework; +using OpenQA.Selenium.BiDi.Modules.BrowsingContext; +using OpenQA.Selenium.BiDi.Modules.Input; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi.Input; + +class CombinedInputActionsTest : BiDiTestFixture +{ + //[Test] + public async Task Paint() + { + driver.Url = "https://kleki.com/"; + + await Task.Delay(3000); + + await context.Input.PerformActionsAsync([new PointerActions { + new Pointer.Move(300, 300), + new Pointer.Down(0), + new Pointer.Move(400, 400) { Duration = 2000, Width = 1, Twist = 1 }, + new Pointer.Up(0), + }]); + + await context.Input.PerformActionsAsync([new KeyActions { + new Key.Down('U'), + new Key.Up('U'), + new Pause { Duration = 3000 } + }]); + + await context.Input.PerformActionsAsync([new PointerActions { + new Pointer.Move(300, 300), + new Pointer.Down(0), + new Pointer.Move(400, 400) { Duration = 2000 }, + new Pointer.Up(0), + }]); + + await Task.Delay(3000); + } + + [Test] + public async Task TestShiftClickingOnMultiSelectionList() + { + driver.Url = UrlBuilder.WhereIs("formSelectionPage.html"); + + var options = await context.LocateNodesAsync(new Locator.Css("option")); + + await context.Input.PerformActionsAsync([ + new PointerActions + { + new Pointer.Down(1), + new Pointer.Up(1), + } + ]); + } +} diff --git a/dotnet/test/common/BiDi/Input/DefaultKeyboardTest.cs b/dotnet/test/common/BiDi/Input/DefaultKeyboardTest.cs new file mode 100644 index 0000000000000..f226719e17c0a --- /dev/null +++ b/dotnet/test/common/BiDi/Input/DefaultKeyboardTest.cs @@ -0,0 +1,88 @@ +//using NUnit.Framework; +//using OpenQA.Selenium.BiDi.Modules.BrowsingContext; +//using OpenQA.Selenium.BiDi.Modules.Input; +//using System.Threading.Tasks; + +//namespace OpenQA.Selenium.BiDi.Input; + +//class DefaultKeyboardTest : BiDiTestFixture +//{ +// [Test] +// public async Task TestBasicKeyboardInput() +// { +// driver.Url = UrlBuilder.WhereIs("single_text_input.html"); + +// var input = (await context.LocateNodesAsync(new Locator.Css("#textInput")))[0]; + +// await context.Input.PerformActionsAsync(new SequentialSourceActions() +// .PointerMove(0, 0, new() { Origin = new Modules.Input.Origin.Element(new Modules.Script.SharedReference(input.SharedId)) }) +// .PointerDown(0) +// .PointerUp(0) +// .Type("abc def")); + +// Assert.That(driver.FindElement(By.Id("textInput")).GetAttribute("value"), Is.EqualTo("abc def")); +// } + +// [Test] +// public async Task TestSendingKeyDownOnly() +// { +// driver.Url = UrlBuilder.WhereIs("key_logger.html"); + +// var input = (await context.LocateNodesAsync(new Locator.Css("#theworks")))[0]; + +// await context.Input.PerformActionsAsync(new SequentialSourceActions() +// .PointerMove(0, 0, new() { Origin = new Modules.Input.Origin.Element(new Modules.Script.SharedReference(input.SharedId)) }) +// .PointerDown(0) +// .PointerUp(0) +// .KeyDown(Key.Shift)); + +// Assert.That(driver.FindElement(By.Id("result")).Text, Does.EndWith("keydown")); +// } + +// [Test] +// public async Task TestSendingKeyUp() +// { +// driver.Url = UrlBuilder.WhereIs("key_logger.html"); + +// var input = (await context.LocateNodesAsync(new Locator.Css("#theworks")))[0]; + +// await context.Input.PerformActionsAsync(new SequentialSourceActions() +// .PointerMove(0, 0, new() { Origin = new Modules.Input.Origin.Element(new Modules.Script.SharedReference(input.SharedId)) }) +// .PointerDown(0) +// .PointerUp(0) +// .KeyDown(Key.Shift) +// .KeyUp(Key.Shift)); + +// Assert.That(driver.FindElement(By.Id("result")).Text, Does.EndWith("keyup")); +// } + +// [Test] +// public async Task TestSendingKeysWithShiftPressed() +// { +// driver.Url = UrlBuilder.WhereIs("key_logger.html"); + +// var input = (await context.LocateNodesAsync(new Locator.Css("#theworks")))[0]; + +// await context.Input.PerformActionsAsync(new SequentialSourceActions() +// .PointerMove(0, 0, new() { Origin = new Modules.Input.Origin.Element(new Modules.Script.SharedReference(input.SharedId)) }) +// .PointerDown(0) +// .PointerUp(0) +// .KeyDown(Key.Shift) +// .Type("ab") +// .KeyUp(Key.Shift)); + +// Assert.That(driver.FindElement(By.Id("result")).Text, Does.EndWith("keydown keydown keypress keyup keydown keypress keyup keyup")); +// Assert.That(driver.FindElement(By.Id("theworks")).GetAttribute("value"), Is.EqualTo("AB")); +// } + +// [Test] +// public async Task TestSendingKeysToActiveElement() +// { +// driver.Url = UrlBuilder.WhereIs("bodyTypingTest.html"); + +// await context.Input.PerformActionsAsync(new SequentialSourceActions().Type("ab")); + +// Assert.That(driver.FindElement(By.Id("body_result")).Text, Does.EndWith("keypress keypress")); +// Assert.That(driver.FindElement(By.Id("result")).Text, Is.EqualTo(" ")); +// } +//} diff --git a/dotnet/test/common/BiDi/Input/DefaultMouseTest.cs b/dotnet/test/common/BiDi/Input/DefaultMouseTest.cs new file mode 100644 index 0000000000000..c7408a83101f1 --- /dev/null +++ b/dotnet/test/common/BiDi/Input/DefaultMouseTest.cs @@ -0,0 +1,49 @@ +//using NUnit.Framework; +//using OpenQA.Selenium.BiDi.Modules.BrowsingContext; +//using OpenQA.Selenium.BiDi.Modules.Input; +//using System.Threading.Tasks; + +//namespace OpenQA.Selenium.BiDi.Input; + +//class DefaultMouseTest : BiDiTestFixture +//{ +// [Test] +// public async Task PerformDragAndDropWithMouse() +// { +// driver.Url = UrlBuilder.WhereIs("draggableLists.html"); + +// await context.Input.PerformActionsAsync([ +// new KeyActions +// { +// Actions = +// { +// new Key.Down('A'), +// new Key.Up('B') +// } +// } +// ]); + +// await context.Input.PerformActionsAsync([new KeyActions +// { +// new Key.Down('A'), +// new Key.Down('B'), +// new Pause() +// }]); + +// await context.Input.PerformActionsAsync([new PointerActions +// { +// new Pointer.Down(0), +// new Pointer.Up(0), +// }]); +// } + +// //[Test] +// public async Task PerformCombined() +// { +// await context.NavigateAsync("https://nuget.org", new() { Wait = ReadinessState.Complete }); + +// await context.Input.PerformActionsAsync(new SequentialSourceActions().Type("Hello").Pause(2000).KeyDown(Key.Shift).Type("World")); + +// await Task.Delay(3000); +// } +//} diff --git a/dotnet/test/common/BiDi/Log/LogTest.cs b/dotnet/test/common/BiDi/Log/LogTest.cs new file mode 100644 index 0000000000000..f67621bfc3b43 --- /dev/null +++ b/dotnet/test/common/BiDi/Log/LogTest.cs @@ -0,0 +1,75 @@ +using NUnit.Framework; +using OpenQA.Selenium.BiDi.Modules.Log; +using System; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi.Log; + +class LogTest : BiDiTestFixture +{ + [Test] + public async Task CanListenToConsoleLog() + { + TaskCompletionSource tcs = new(); + + await using var subscription = await context.Log.OnEntryAddedAsync(tcs.SetResult); + + driver.Url = UrlBuilder.WhereIs("bidi/logEntryAdded.html"); + driver.FindElement(By.Id("consoleLog")).Click(); + + var logEntry = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(logEntry, Is.Not.Null); + Assert.That(logEntry.Source, Is.Not.Null); + Assert.That(logEntry.Source.Context, Is.EqualTo(context)); + Assert.That(logEntry.Source.Realm, Is.Not.Null); + Assert.That(logEntry.Text, Is.EqualTo("Hello, world!")); + Assert.That(logEntry.Level, Is.EqualTo(Level.Info)); + Assert.That(logEntry, Is.AssignableFrom()); + + var consoleLogEntry = logEntry as Entry.Console; + + Assert.That(consoleLogEntry.Method, Is.EqualTo("log")); + + Assert.That(consoleLogEntry.Args, Is.Not.Null); + Assert.That(consoleLogEntry.Args, Has.Count.EqualTo(1)); + Assert.That(consoleLogEntry.Args[0], Is.AssignableFrom()); + } + + [Test] + public async Task CanListenToJavascriptLog() + { + TaskCompletionSource tcs = new(); + + await using var subscription = await context.Log.OnEntryAddedAsync(tcs.SetResult); + + driver.Url = UrlBuilder.WhereIs("bidi/logEntryAdded.html"); + driver.FindElement(By.Id("jsException")).Click(); + + var logEntry = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(logEntry, Is.Not.Null); + Assert.That(logEntry.Source, Is.Not.Null); + Assert.That(logEntry.Source.Context, Is.EqualTo(context)); + Assert.That(logEntry.Source.Realm, Is.Not.Null); + Assert.That(logEntry.Text, Is.EqualTo("Error: Not working")); + Assert.That(logEntry.Level, Is.EqualTo(Level.Error)); + Assert.That(logEntry, Is.AssignableFrom()); + } + + [Test] + public async Task CanRetrieveStacktrace() + { + TaskCompletionSource tcs = new(); + + await using var subscription = await bidi.Log.OnEntryAddedAsync(tcs.SetResult); + + driver.Url = UrlBuilder.WhereIs("bidi/logEntryAdded.html"); + driver.FindElement(By.Id("logWithStacktrace")).Click(); + + var logEntry = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(logEntry, Is.Not.Null); + Assert.That(logEntry.StackTrace, Is.Not.Null); + } +} diff --git a/dotnet/test/common/BiDi/Network/NetworkEventsTest.cs b/dotnet/test/common/BiDi/Network/NetworkEventsTest.cs new file mode 100644 index 0000000000000..230351ba436df --- /dev/null +++ b/dotnet/test/common/BiDi/Network/NetworkEventsTest.cs @@ -0,0 +1,132 @@ +using NUnit.Framework; +using OpenQA.Selenium.BiDi.Modules.BrowsingContext; +using OpenQA.Selenium.BiDi.Modules.Network; +using System; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi.Network; + +class NetworkEventsTest : BiDiTestFixture +{ + [Test] + public async Task CanListenToBeforeRequestSentEvent() + { + TaskCompletionSource tcs = new(); + + await using var subscription = await context.Network.OnBeforeRequestSentAsync(tcs.SetResult); + + await context.NavigateAsync(UrlBuilder.WhereIs("bidi/logEntryAdded.html"), new() { Wait = ReadinessState.Complete }); + + var req = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(req.Context, Is.EqualTo(context)); + Assert.That(req.Request, Is.Not.Null); + Assert.That(req.Request.Method, Is.EqualTo("GET")); + Assert.That(req.Request.Url, Does.Contain("bidi/logEntryAdded.html")); + Assert.That(req.Initiator.Type, Is.EqualTo(InitiatorType.Other)); + } + + [Test] + public async Task CanListenToResponseStartedEvent() + { + TaskCompletionSource tcs = new(); + + await using var subscription = await context.Network.OnResponseStartedAsync(tcs.SetResult); + + await context.NavigateAsync(UrlBuilder.WhereIs("bidi/logEntryAdded.html"), new() { Wait = ReadinessState.Complete }); + + var res = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(res.Context, Is.EqualTo(context)); + Assert.That(res.Request, Is.Not.Null); + Assert.That(res.Request.Method, Is.EqualTo("GET")); + Assert.That(res.Request.Url, Does.Contain("bidi/logEntryAdded.html")); + Assert.That(res.Response.Headers, Is.Not.Empty); + Assert.That(res.Response.Status, Is.EqualTo(200)); + } + + [Test] + public async Task CanListenToResponseCompletedEvent() + { + TaskCompletionSource tcs = new(); + + await using var subscription = await context.Network.OnResponseCompletedAsync(tcs.SetResult); + + await context.NavigateAsync(UrlBuilder.WhereIs("bidi/logEntryAdded.html"), new() { Wait = ReadinessState.Complete }); + + var res = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(res.Context, Is.EqualTo(context)); + Assert.That(res.Request, Is.Not.Null); + Assert.That(res.Request.Method, Is.EqualTo("GET")); + Assert.That(res.Request.Url, Does.Contain("bidi/logEntryAdded.html")); + Assert.That(res.Response.Url, Does.Contain("bidi/logEntryAdded.html")); + Assert.That(res.Response.Headers, Is.Not.Empty); + Assert.That(res.Response.Status, Is.EqualTo(200)); + } + + [Test] + public async Task CanListenToBeforeRequestSentEventWithCookie() + { + TaskCompletionSource tcs = new(); + + await context.NavigateAsync(UrlBuilder.WhereIs("bidi/logEntryAdded.html"), new() { Wait = ReadinessState.Complete }); + + driver.Manage().Cookies.AddCookie(new("foo", "bar")); + + await using var subscription = await bidi.Network.OnBeforeRequestSentAsync(tcs.SetResult); + + await context.ReloadAsync(); + + var req = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(req.Request.Cookies.Count, Is.EqualTo(1)); + Assert.That(req.Request.Cookies[0].Name, Is.EqualTo("foo")); + Assert.That((req.Request.Cookies[0].Value as BytesValue.String).Value, Is.EqualTo("bar")); + } + + [Test] + [IgnoreBrowser(Selenium.Browser.Chrome)] + [IgnoreBrowser(Selenium.Browser.Edge)] + public async Task CanListenToOnAuthRequiredEvent() + { + TaskCompletionSource tcs = new(); + + await using var subscription = await context.Network.OnAuthRequiredAsync(tcs.SetResult); + + driver.Url = UrlBuilder.WhereIs("basicAuth"); + + var res = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(res.Context, Is.EqualTo(context)); + Assert.That(res.Request, Is.Not.Null); + Assert.That(res.Request.Method, Is.EqualTo("GET")); + Assert.That(res.Request.Url, Does.Contain("basicAuth")); + Assert.That(res.Response.Headers, Is.Not.Null.And.Count.GreaterThanOrEqualTo(1)); + Assert.That(res.Response.Status, Is.EqualTo(401)); + } + + [Test] + public async Task CanListenToFetchError() + { + TaskCompletionSource tcs = new(); + + await using var subscription = await context.Network.OnFetchErrorAsync(tcs.SetResult); + + try + { + await context.NavigateAsync("https://not_a_valid_url.test", new() { Wait = ReadinessState.Complete }); + } + catch (Exception) { } + + var res = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(res.Context, Is.EqualTo(context)); + Assert.That(res.Request, Is.Not.Null); + Assert.That(res.Request.Method, Is.EqualTo("GET")); + Assert.That(res.Request.Url, Does.Contain("https://not_a_valid_url.test")); + Assert.That(res.Request.Headers.Count, Is.GreaterThanOrEqualTo(1)); + Assert.That(res.Navigation, Is.Not.Null); + Assert.That(res.ErrorText, Does.Contain("net::ERR_NAME_NOT_RESOLVED").Or.Contain("NS_ERROR_UNKNOWN_HOST")); + } +} diff --git a/dotnet/test/common/BiDi/Network/NetworkTest.cs b/dotnet/test/common/BiDi/Network/NetworkTest.cs new file mode 100644 index 0000000000000..b743378c385ea --- /dev/null +++ b/dotnet/test/common/BiDi/Network/NetworkTest.cs @@ -0,0 +1,195 @@ +using NUnit.Framework; +using OpenQA.Selenium.BiDi.Modules.BrowsingContext; +using OpenQA.Selenium.BiDi.Modules.Network; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi.Network; + +class NetworkTest : BiDiTestFixture +{ + [Test] + public async Task CanAddIntercept() + { + await using var intercept = await bidi.Network.InterceptRequestAsync(e => Task.CompletedTask); + + Assert.That(intercept, Is.Not.Null); + } + + [Test] + public async Task CanAddInterceptStringUrlPattern() + { + await using var intercept = await bidi.Network.InterceptRequestAsync(e => Task.CompletedTask, new() + { + UrlPatterns = [ + new UrlPattern.String("http://localhost:4444"), + "http://localhost:4444/" + ] + }); + + Assert.That(intercept, Is.Not.Null); + } + + [Test] + public async Task CanAddInterceptUrlPattern() + { + await using var intercept = await bidi.Network.InterceptRequestAsync(e => Task.CompletedTask, interceptOptions: new() + { + UrlPatterns = [new UrlPattern.Pattern() + { + Hostname = "localhost", + Protocol = "http" + }] + }); + + Assert.That(intercept, Is.Not.Null); + } + + [Test] + public async Task CanContinueRequest() + { + int times = 0; + await using var intercept = await bidi.Network.InterceptRequestAsync(async e => + { + times++; + + await e.Request.Request.ContinueAsync(); + }); + + await context.NavigateAsync(UrlBuilder.WhereIs("bidi/logEntryAdded.html"), new() { Wait = ReadinessState.Complete }); + + Assert.That(intercept, Is.Not.Null); + Assert.That(times, Is.GreaterThan(0)); + } + + [Test] + public async Task CanContinueResponse() + { + int times = 0; + + await using var intercept = await bidi.Network.InterceptResponseAsync(async e => + { + times++; + + await e.Request.Request.ContinueResponseAsync(); + }); + + await context.NavigateAsync(UrlBuilder.WhereIs("bidi/logEntryAdded.html"), new() { Wait = ReadinessState.Complete }); + + Assert.That(intercept, Is.Not.Null); + Assert.That(times, Is.GreaterThan(0)); + } + + [Test] + public async Task CanProvideResponse() + { + int times = 0; + + await using var intercept = await bidi.Network.InterceptRequestAsync(async e => + { + times++; + + await e.Request.Request.ProvideResponseAsync(); + }); + + await context.NavigateAsync(UrlBuilder.WhereIs("bidi/logEntryAdded.html"), new() { Wait = ReadinessState.Complete }); + + Assert.That(intercept, Is.Not.Null); + Assert.That(times, Is.GreaterThan(0)); + } + + [Test] + public async Task CanProvideResponseWithParameters() + { + int times = 0; + + await using var intercept = await bidi.Network.InterceptRequestAsync(async e => + { + times++; + + await e.Request.Request.ProvideResponseAsync(new() { Body = """ + + + Hello + + + + + """ }); + }); + + await context.NavigateAsync(UrlBuilder.WhereIs("bidi/logEntryAdded.html"), new() { Wait = ReadinessState.Complete }); + + Assert.That(intercept, Is.Not.Null); + Assert.That(times, Is.GreaterThan(0)); + Assert.That(driver.Title, Is.EqualTo("Hello")); + } + + [Test] + public async Task CanRemoveIntercept() + { + var intercept = await bidi.Network.InterceptRequestAsync(_ => Task.CompletedTask); + + await intercept.RemoveAsync(); + + // or + + intercept = await context.Network.InterceptRequestAsync(_ => Task.CompletedTask); + + await intercept.DisposeAsync(); + } + + [Test] + public async Task CanContinueWithAuthCredentials() + { + await using var intercept = await bidi.Network.InterceptAuthAsync(async e => + { + //TODO Seems it would be better to have method which takes abstract options + await e.Request.Request.ContinueWithAuthAsync(new AuthCredentials.Basic("test", "test")); + }); + + await context.NavigateAsync(UrlBuilder.WhereIs("basicAuth"), new() { Wait = ReadinessState.Complete }); + + Assert.That(driver.FindElement(By.CssSelector("h1")).Text, Is.EqualTo("authorized")); + } + + [Test] + [IgnoreBrowser(Selenium.Browser.Firefox)] + public async Task CanContinueWithDefaultCredentials() + { + await using var intercept = await bidi.Network.InterceptAuthAsync(async e => + { + await e.Request.Request.ContinueWithAuthAsync(new ContinueWithDefaultAuthOptions()); + }); + + var action = async () => await context.NavigateAsync(UrlBuilder.WhereIs("basicAuth"), new() { Wait = ReadinessState.Complete }); + + Assert.That(action, Throws.TypeOf().With.Message.Contain("net::ERR_INVALID_AUTH_CREDENTIALS")); + } + + [Test] + [IgnoreBrowser(Selenium.Browser.Firefox)] + public async Task CanContinueWithCanceledCredentials() + { + await using var intercept = await bidi.Network.InterceptAuthAsync(async e => + { + await e.Request.Request.ContinueWithAuthAsync(new ContinueWithCancelledAuthOptions()); + }); + + var action = async () => await context.NavigateAsync(UrlBuilder.WhereIs("basicAuth"), new() { Wait = ReadinessState.Complete }); + + Assert.That(action, Throws.TypeOf().With.Message.Contain("net::ERR_HTTP_RESPONSE_CODE_FAILURE")); + } + + [Test] + public async Task CanFailRequest() + { + await using var intercept = await bidi.Network.InterceptRequestAsync(async e => + { + await e.Request.Request.FailAsync(); + }); + + var action = async () => await context.NavigateAsync(UrlBuilder.WhereIs("basicAuth"), new() { Wait = ReadinessState.Complete }); + + Assert.That(action, Throws.TypeOf().With.Message.Contain("net::ERR_FAILED").Or.Message.Contain("NS_ERROR_ABORT")); + } +} diff --git a/dotnet/test/common/BiDi/Script/CallFunctionParameterTest.cs b/dotnet/test/common/BiDi/Script/CallFunctionParameterTest.cs new file mode 100644 index 0000000000000..3fc11764fca2e --- /dev/null +++ b/dotnet/test/common/BiDi/Script/CallFunctionParameterTest.cs @@ -0,0 +1,203 @@ +using NUnit.Framework; +using OpenQA.Selenium.BiDi.Modules.Script; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi.Script; + +class CallFunctionParameterTest : BiDiTestFixture +{ + [Test] + public async Task CanCallFunctionWithDeclaration() + { + var res = await context.Script.CallFunctionAsync("() => { return 1 + 2; }", false); + + Assert.That(res, Is.Not.Null); + Assert.That(res.Realm, Is.Not.Null); + Assert.That((res.Result as RemoteValue.Number).Value, Is.EqualTo(3)); + } + + [Test] + public async Task CanCallFunctionWithDeclarationImplicitCast() + { + var res = await context.Script.CallFunctionAsync("() => { return 1 + 2; }", false); + + Assert.That(res, Is.EqualTo(3)); + } + + [Test] + public async Task CanEvaluateScriptWithUserActivationTrue() + { + await context.Script.EvaluateAsync("window.open();", true, new() { UserActivation = true }); + + var res = await context.Script.CallFunctionAsync(""" + () => navigator.userActivation.isActive && navigator.userActivation.hasBeenActive + """, true, new() { UserActivation = true }); + + Assert.That(res, Is.True); + } + + [Test] + public async Task CanEvaluateScriptWithUserActivationFalse() + { + await context.Script.EvaluateAsync("window.open();", true); + + var res = await context.Script.CallFunctionAsync(""" + () => navigator.userActivation.isActive && navigator.userActivation.hasBeenActive + """, true); + + Assert.That(res, Is.False); + } + + [Test] + public async Task CanCallFunctionWithArguments() + { + var res = await context.Script.CallFunctionAsync("(...args)=>{return args}", false, new() + { + Arguments = ["abc", 42] + }); + + Assert.That(res.Result, Is.AssignableFrom()); + Assert.That((string)(res.Result as RemoteValue.Array).Value[0], Is.EqualTo("abc")); + Assert.That((int)(res.Result as RemoteValue.Array).Value[1], Is.EqualTo(42)); + } + + [Test] + public async Task CanCallFunctionToGetIFrameBrowsingContext() + { + driver.Url = UrlBuilder.WhereIs("click_too_big_in_frame.html"); + + var res = await context.Script.CallFunctionAsync(""" + () => document.querySelector('iframe[id="iframe1"]').contentWindow + """, false); + + Assert.That(res, Is.Not.Null); + Assert.That(res.Result, Is.AssignableFrom()); + Assert.That((res.Result as RemoteValue.WindowProxy).Value, Is.Not.Null); + } + + [Test] + public async Task CanCallFunctionToGetElement() + { + driver.Url = UrlBuilder.WhereIs("bidi/logEntryAdded.html"); + + var res = await context.Script.CallFunctionAsync(""" + () => document.getElementById("consoleLog") + """, false); + + Assert.That(res, Is.Not.Null); + Assert.That(res.Result, Is.AssignableFrom()); + Assert.That((res.Result as RemoteValue.Node).Value, Is.Not.Null); + } + + [Test] + public async Task CanCallFunctionWithAwaitPromise() + { + var res = await context.Script.CallFunctionAsync(""" + async function() { + await new Promise(r => setTimeout(() => r(), 0)); + return "SOME_DELAYED_RESULT"; + } + """, awaitPromise: true); + + Assert.That(res, Is.EqualTo("SOME_DELAYED_RESULT")); + } + + [Test] + public async Task CanCallFunctionWithAwaitPromiseFalse() + { + var res = await context.Script.CallFunctionAsync(""" + async function() { + await new Promise(r => setTimeout(() => r(), 0)); + return "SOME_DELAYED_RESULT"; + } + """, awaitPromise: false); + + Assert.That(res, Is.Not.Null); + Assert.That(res.Result, Is.AssignableFrom()); + } + + [Test] + public async Task CanCallFunctionWithThisParameter() + { + var thisParameter = new LocalValue.Object([["some_property", 42]]); + + var res = await context.Script.CallFunctionAsync(""" + function(){return this.some_property} + """, false, new() { This = thisParameter }); + + Assert.That(res, Is.EqualTo(42)); + } + + [Test] + public async Task CanCallFunctionWithOwnershipRoot() + { + var res = await context.Script.CallFunctionAsync("async function(){return {a:1}}", true, new() + { + ResultOwnership = ResultOwnership.Root + }); + + Assert.That(res, Is.Not.Null); + Assert.That((res.Result as RemoteValue.Object).Handle, Is.Not.Null); + Assert.That((string)(res.Result as RemoteValue.Object).Value[0][0], Is.EqualTo("a")); + Assert.That((int)(res.Result as RemoteValue.Object).Value[0][1], Is.EqualTo(1)); + } + + [Test] + public async Task CanCallFunctionWithOwnershipNone() + { + var res = await context.Script.CallFunctionAsync("async function(){return {a:1}}", true, new() + { + ResultOwnership = ResultOwnership.None + }); + + Assert.That(res, Is.Not.Null); + Assert.That((res.Result as RemoteValue.Object).Handle, Is.Null); + Assert.That((string)(res.Result as RemoteValue.Object).Value[0][0], Is.EqualTo("a")); + Assert.That((int)(res.Result as RemoteValue.Object).Value[0][1], Is.EqualTo(1)); + } + + [Test] + public void CanCallFunctionThatThrowsException() + { + var action = () => context.Script.CallFunctionAsync("))) !!@@## some invalid JS script (((", false); + + Assert.That(action, Throws.InstanceOf().And.Message.Contain("SyntaxError:")); + } + + [Test] + public async Task CanCallFunctionInASandBox() + { + // Make changes without sandbox + await context.Script.CallFunctionAsync("() => { window.foo = 1; }", true); + + var res = await context.Script.CallFunctionAsync("() => window.foo", true, targetOptions: new() { Sandbox = "sandbox" }); + + Assert.That(res.Result, Is.AssignableFrom()); + + // Make changes in the sandbox + await context.Script.CallFunctionAsync("() => { window.foo = 2; }", true, targetOptions: new() { Sandbox = "sandbox" }); + + // Check if the changes are present in the sandbox + res = await context.Script.CallFunctionAsync("() => window.foo", true, targetOptions: new() { Sandbox = "sandbox" }); + + Assert.That(res.Result, Is.AssignableFrom()); + Assert.That((res.Result as RemoteValue.Number).Value, Is.EqualTo(2)); + } + + [Test] + public async Task CanCallFunctionInARealm() + { + await bidi.BrowsingContext.CreateAsync(Modules.BrowsingContext.ContextType.Tab); + + var realms = await bidi.Script.GetRealmsAsync(); + + await bidi.Script.CallFunctionAsync("() => { window.foo = 3; }", true, new Target.Realm(realms[0].Realm)); + await bidi.Script.CallFunctionAsync("() => { window.foo = 5; }", true, new Target.Realm(realms[1].Realm)); + + var res1 = await bidi.Script.CallFunctionAsync("() => window.foo", true, new Target.Realm(realms[0].Realm)); + var res2 = await bidi.Script.CallFunctionAsync("() => window.foo", true, new Target.Realm(realms[1].Realm)); + + Assert.That(res1, Is.EqualTo(3)); + Assert.That(res2, Is.EqualTo(5)); + } +} diff --git a/dotnet/test/common/BiDi/Script/EvaluateParametersTest.cs b/dotnet/test/common/BiDi/Script/EvaluateParametersTest.cs new file mode 100644 index 0000000000000..bb526ff9a4d89 --- /dev/null +++ b/dotnet/test/common/BiDi/Script/EvaluateParametersTest.cs @@ -0,0 +1,109 @@ +using NUnit.Framework; +using OpenQA.Selenium.BiDi.Modules.Script; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi.Script; + +class EvaluateParametersTest : BiDiTestFixture +{ + [Test] + public async Task CanEvaluateScript() + { + var res = await context.Script.EvaluateAsync("1 + 2", false); + + Assert.That(res, Is.Not.Null); + Assert.That(res.Realm, Is.Not.Null); + Assert.That((res.Result as RemoteValue.Number).Value, Is.EqualTo(3)); + } + + [Test] + public async Task CanEvaluateScriptImplicitCast() + { + var res = await context.Script.EvaluateAsync("1 + 2", false); + + Assert.That(res, Is.EqualTo(3)); + } + + [Test] + public async Task СanEvaluateScriptWithUserActivationTrue() + { + await context.Script.EvaluateAsync("window.open();", true, new() { UserActivation = true }); + + var res = await context.Script.EvaluateAsync(""" + navigator.userActivation.isActive && navigator.userActivation.hasBeenActive + """, true, new() { UserActivation = true }); + + Assert.That(res, Is.True); + } + + [Test] + public async Task СanEvaluateScriptWithUserActivationFalse() + { + await context.Script.EvaluateAsync("window.open();", true, new() { UserActivation = true }); + + var res = await context.Script.EvaluateAsync(""" + navigator.userActivation.isActive && navigator.userActivation.hasBeenActive + """, true, new() { UserActivation = false }); + + Assert.That(res, Is.False); + } + + [Test] + public void CanCallFunctionThatThrowsException() + { + var action = () => context.Script.EvaluateAsync("))) !!@@## some invalid JS script (((", false); + + Assert.That(action, Throws.InstanceOf().And.Message.Contain("SyntaxError:")); + } + + [Test] + public async Task CanEvaluateScriptWithResulWithOwnership() + { + var res = await context.Script.EvaluateAsync("Promise.resolve({a:1})", true, new() + { + ResultOwnership = ResultOwnership.Root + }); + + Assert.That(res, Is.Not.Null); + Assert.That((res.Result as RemoteValue.Object).Handle, Is.Not.Null); + Assert.That((string)(res.Result as RemoteValue.Object).Value[0][0], Is.EqualTo("a")); + Assert.That((int)(res.Result as RemoteValue.Object).Value[0][1], Is.EqualTo(1)); + } + + [Test] + public async Task CanEvaluateInASandBox() + { + // Make changes without sandbox + await context.Script.EvaluateAsync("window.foo = 1", true); + + var res = await context.Script.EvaluateAsync("window.foo", true, targetOptions: new() { Sandbox = "sandbox" }); + + Assert.That(res.Result, Is.AssignableFrom()); + + // Make changes in the sandbox + await context.Script.EvaluateAsync("window.foo = 2", true, targetOptions: new() { Sandbox = "sandbox" }); + + // Check if the changes are present in the sandbox + res = await context.Script.EvaluateAsync("window.foo", true, targetOptions: new() { Sandbox = "sandbox" }); + + Assert.That(res.Result, Is.AssignableFrom()); + Assert.That((res.Result as RemoteValue.Number).Value, Is.EqualTo(2)); + } + + [Test] + public async Task CanEvaluateInARealm() + { + await bidi.BrowsingContext.CreateAsync(Modules.BrowsingContext.ContextType.Tab); + + var realms = await bidi.Script.GetRealmsAsync(); + + await bidi.Script.EvaluateAsync("window.foo = 3", true, new Target.Realm(realms[0].Realm)); + await bidi.Script.EvaluateAsync("window.foo = 5", true, new Target.Realm(realms[1].Realm)); + + var res1 = await bidi.Script.EvaluateAsync("window.foo", true, new Target.Realm(realms[0].Realm)); + var res2 = await bidi.Script.EvaluateAsync("window.foo", true, new Target.Realm(realms[1].Realm)); + + Assert.That(res1, Is.EqualTo(3)); + Assert.That(res2, Is.EqualTo(5)); + } +} diff --git a/dotnet/test/common/BiDi/Script/ScriptCommandsTest.cs b/dotnet/test/common/BiDi/Script/ScriptCommandsTest.cs new file mode 100644 index 0000000000000..c013fbbbed2d4 --- /dev/null +++ b/dotnet/test/common/BiDi/Script/ScriptCommandsTest.cs @@ -0,0 +1,150 @@ +using NUnit.Framework; +using OpenQA.Selenium.BiDi.Modules.Script; +using System; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi.Script; + +class ScriptCommandsTest : BiDiTestFixture +{ + [Test] + public async Task CanGetAllRealms() + { + _ = await bidi.BrowsingContext.CreateAsync(Modules.BrowsingContext.ContextType.Window); + + var realms = await bidi.Script.GetRealmsAsync(); + + Assert.That(realms, Is.Not.Null); + Assert.That(realms.Count, Is.EqualTo(2)); + + Assert.That(realms[0], Is.AssignableFrom()); + Assert.That(realms[0].Realm, Is.Not.Null); + + Assert.That(realms[1], Is.AssignableFrom()); + Assert.That(realms[1].Realm, Is.Not.Null); + } + + [Test] + public async Task CanGetAllRealmsByType() + { + _ = await bidi.BrowsingContext.CreateAsync(Modules.BrowsingContext.ContextType.Window); + + var realms = await bidi.Script.GetRealmsAsync(new() { Type = RealmType.Window }); + + Assert.That(realms, Is.Not.Null); + Assert.That(realms.Count, Is.EqualTo(2)); + + Assert.That(realms[0], Is.AssignableFrom()); + Assert.That(realms[0].Realm, Is.Not.Null); + + Assert.That(realms[1], Is.AssignableFrom()); + Assert.That(realms[1].Realm, Is.Not.Null); + } + + [Test] + public async Task CanGetRealmInBrowsingContext() + { + var tab = await bidi.BrowsingContext.CreateAsync(Modules.BrowsingContext.ContextType.Tab); + + var realms = await tab.Script.GetRealmsAsync(); + + var tabRealm = realms[0] as RealmInfo.Window; + + Assert.That(tabRealm, Is.Not.Null); + Assert.That(tabRealm.Context, Is.EqualTo(tab)); + } + + [Test] + public async Task CanGetRealmInBrowsingContextByType() + { + var tab = await bidi.BrowsingContext.CreateAsync(Modules.BrowsingContext.ContextType.Tab); + + var realms = await tab.Script.GetRealmsAsync(new() { Type = RealmType.Window }); + + var tabRealm = realms[0] as RealmInfo.Window; + + Assert.That(tabRealm, Is.Not.Null); + Assert.That(tabRealm.Context, Is.EqualTo(tab)); + } + + [Test] + public async Task CanAddPreloadScript() + { + var preloadScript = await bidi.Script.AddPreloadScriptAsync("() => { console.log('preload_script_console_text') }"); + + Assert.That(preloadScript, Is.Not.Null); + + TaskCompletionSource tcs = new(); + + await context.Log.OnEntryAddedAsync(tcs.SetResult); + + await context.ReloadAsync(new() { Wait = Modules.BrowsingContext.ReadinessState.Interactive }); + + var entry = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(entry.Level, Is.EqualTo(Modules.Log.Level.Info)); + Assert.That(entry.Text, Is.EqualTo("preload_script_console_text")); + } + + [Test] + public async Task CanAddPreloadScriptWithArguments() + { + var preloadScript = await bidi.Script.AddPreloadScriptAsync("(channel) => channel('will_be_send', 'will_be_ignored')", new() + { + Arguments = [new LocalValue.Channel(new(new("channel_name")))] + }); + + Assert.That(preloadScript, Is.Not.Null); + } + + + [Test] + public async Task CanAddPreloadScriptWithChannelOptions() + { + var preloadScript = await bidi.Script.AddPreloadScriptAsync("(channel) => channel('will_be_send', 'will_be_ignored')", new() + { + Arguments = [new LocalValue.Channel(new(new("channel_name")) + { + SerializationOptions = new() + { + MaxDomDepth = 0 + }, + Ownership = ResultOwnership.Root + })] + }); + + Assert.That(preloadScript, Is.Not.Null); + } + + [Test] + public async Task CanAddPreloadScriptInASandbox() + { + var preloadScript = await bidi.Script.AddPreloadScriptAsync("() => { window.bar = 2; }", new() { Sandbox = "sandbox" }); + + Assert.That(preloadScript, Is.Not.Null); + + await context.ReloadAsync(new() { Wait = Modules.BrowsingContext.ReadinessState.Interactive }); + + var bar = await context.Script.EvaluateAsync("window.bar", true, targetOptions: new() { Sandbox = "sandbox" }); + + Assert.That(bar, Is.EqualTo(2)); + } + + [Test] + public async Task CanRemovePreloadedScript() + { + var preloadScript = await context.Script.AddPreloadScriptAsync("() => { window.bar = 2; }"); + + await context.ReloadAsync(new() { Wait = Modules.BrowsingContext.ReadinessState.Interactive }); + + var bar = await context.Script.EvaluateAsync("window.bar", true); + + Assert.That(bar, Is.EqualTo(2)); + + await preloadScript.RemoveAsync(); + + var resultAfterRemoval = await context.Script.EvaluateAsync("window.bar", true, targetOptions: new() { Sandbox = "sandbox" }); + + Assert.That(resultAfterRemoval.Result, Is.AssignableFrom()); + } +} diff --git a/dotnet/test/common/BiDi/Script/ScriptEventsTest.cs b/dotnet/test/common/BiDi/Script/ScriptEventsTest.cs new file mode 100644 index 0000000000000..48fc3b5686371 --- /dev/null +++ b/dotnet/test/common/BiDi/Script/ScriptEventsTest.cs @@ -0,0 +1,63 @@ +using NUnit.Framework; +using OpenQA.Selenium.BiDi.Modules.Script; +using System; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi.Script; + +class ScriptEventsTest : BiDiTestFixture +{ + [Test] + public async Task CanListenToChannelMessage() + { + TaskCompletionSource tcs = new(); + + await bidi.Script.OnMessageAsync(tcs.SetResult); + + await context.Script.CallFunctionAsync("(channel) => channel('foo')", false, new() + { + Arguments = [new LocalValue.Channel(new(new("channel_name")))] + }); + + var message = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(message, Is.Not.Null); + Assert.That(message.Channel.Id, Is.EqualTo("channel_name")); + Assert.That((string)message.Data, Is.EqualTo("foo")); + Assert.That(message.Source, Is.Not.Null); + Assert.That(message.Source.Realm, Is.Not.Null); + Assert.That(message.Source.Context, Is.EqualTo(context)); + } + + [Test] + public async Task CanListenToRealmCreatedEvent() + { + TaskCompletionSource tcs = new(); + + await bidi.Script.OnRealmCreatedAsync(tcs.SetResult); + + await bidi.BrowsingContext.CreateAsync(Modules.BrowsingContext.ContextType.Window); + + var realmInfo = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(realmInfo, Is.Not.Null); + Assert.That(realmInfo, Is.AssignableFrom()); + Assert.That(realmInfo.Realm, Is.Not.Null); + } + + [Test] + public async Task CanListenToRealmDestroyedEvent() + { + TaskCompletionSource tcs = new(); + + await bidi.Script.OnRealmDestroyedAsync(tcs.SetResult); + + var ctx = await bidi.BrowsingContext.CreateAsync(Modules.BrowsingContext.ContextType.Window); + await ctx.CloseAsync(); + + var args = await tcs.Task.WaitAsync(TimeSpan.FromSeconds(5)); + + Assert.That(args, Is.Not.Null); + Assert.That(args.Realm, Is.Not.Null); + } +} diff --git a/dotnet/test/common/BiDi/Storage/StorageTest.cs b/dotnet/test/common/BiDi/Storage/StorageTest.cs new file mode 100644 index 0000000000000..2f9877c4ca1aa --- /dev/null +++ b/dotnet/test/common/BiDi/Storage/StorageTest.cs @@ -0,0 +1,161 @@ +using NUnit.Framework; +using OpenQA.Selenium.BiDi.Modules.Network; +using System; +using System.Threading.Tasks; + +namespace OpenQA.Selenium.BiDi.Storage; + +class StorageTest : BiDiTestFixture +{ + [Test] + public async Task CanGetCookieByName() + { + driver.Url = UrlBuilder.WhereIs("animals"); + + var cookies = await bidi.Storage.GetCookiesAsync(new() + { + Filter = new() + { + Name = Guid.NewGuid().ToString(), + Value = "set" + } + }); + + Assert.That(cookies, Is.Not.Null); + Assert.That(cookies, Is.Empty); + } + + [Test] + public async Task CanGetCookieInDefaultUserContext() + { + driver.Url = UrlBuilder.WhereIs("animals"); + + var userContexts = await bidi.Browser.GetUserContextsAsync(); + + var cookies = await context.Storage.GetCookiesAsync(new() + { + Filter = new() + { + Name = Guid.NewGuid().ToString(), + Value = "set" + } + }); + + Assert.That(cookies, Is.Not.Null); + Assert.That(cookies, Is.Empty); + Assert.That(cookies.PartitionKey.UserContext, Is.EqualTo(userContexts[0].UserContext)); + } + + [Test] + public async Task CanAddCookie() + { + driver.Url = UrlBuilder.WhereIs("animals"); + + var partitionKey = await context.Storage.SetCookieAsync(new("fish", "cod", UrlBuilder.HostName)); + + Assert.That(partitionKey, Is.Not.Null); + } + + [Test] + public async Task CanAddAndGetCookie() + { + driver.Url = UrlBuilder.WhereIs("animals"); + + var expiry = DateTime.Now.AddDays(1); + + await context.Storage.SetCookieAsync(new("fish", "cod", UrlBuilder.HostName) + { + Path = "/common/animals", + HttpOnly = true, + Secure = false, + SameSite = SameSite.Lax, + Expiry = expiry + }); + + var cookies = await context.Storage.GetCookiesAsync(); + + Assert.That(cookies, Is.Not.Null); + Assert.That(cookies.Count, Is.EqualTo(1)); + + var cookie = cookies[0]; + + Assert.That(cookie.Name, Is.EqualTo("fish")); + Assert.That((cookie.Value as BytesValue.String).Value, Is.EqualTo("cod")); + Assert.That(cookie.Path, Is.EqualTo("/common/animals")); + Assert.That(cookie.HttpOnly, Is.True); + Assert.That(cookie.Secure, Is.False); + Assert.That(cookie.SameSite, Is.EqualTo(SameSite.Lax)); + Assert.That(cookie.Size, Is.EqualTo(7)); + // Assert.That(cookie.Expiry, Is.EqualTo(expiry)); // chrome issue + } + + [Test] + public async Task CanGetAllCookies() + { + driver.Url = UrlBuilder.WhereIs("animals"); + + driver.Manage().Cookies.AddCookie(new("key1", "value1")); + driver.Manage().Cookies.AddCookie(new("key2", "value2")); + + var cookies = await bidi.Storage.GetCookiesAsync(); + + Assert.That(cookies, Is.Not.Null); + Assert.That(cookies.Count, Is.EqualTo(2)); + Assert.That(cookies[0].Name, Is.EqualTo("key1")); + Assert.That(cookies[1].Name, Is.EqualTo("key2")); + } + + [Test] + public async Task CanDeleteAllCookies() + { + driver.Url = UrlBuilder.WhereIs("animals"); + + driver.Manage().Cookies.AddCookie(new("key1", "value1")); + driver.Manage().Cookies.AddCookie(new("key2", "value2")); + + var result = await bidi.Storage.DeleteCookiesAsync(); + + Assert.That(result, Is.Not.Null); + + var cookies = await bidi.Storage.GetCookiesAsync(); + + Assert.That(cookies, Is.Not.Null); + Assert.That(cookies.Count, Is.EqualTo(0)); + } + + [Test] + public async Task CanDeleteCookieWithName() + { + driver.Url = UrlBuilder.WhereIs("animals"); + + driver.Manage().Cookies.AddCookie(new("key1", "value1")); + driver.Manage().Cookies.AddCookie(new("key2", "value2")); + + var result = await bidi.Storage.DeleteCookiesAsync(new() { Filter = new() { Name = "key1" } }); + + Assert.That(result, Is.Not.Null); + + var cookies = await bidi.Storage.GetCookiesAsync(); + + Assert.That(cookies, Is.Not.Null); + Assert.That(cookies.Count, Is.EqualTo(1)); + Assert.That(cookies[0].Name, Is.EqualTo("key2")); + } + + [Test] + public async Task AddCookiesWithDifferentPathsThatAreRelatedToOurs() + { + driver.Url = UrlBuilder.WhereIs("animals"); + + await context.Storage.SetCookieAsync(new("fish", "cod", UrlBuilder.HostName) + { + Path = "/common/animals" + }); + + driver.Url = UrlBuilder.WhereIs("simpleTest"); + + var result = driver.Manage().Cookies.AllCookies; + + Assert.That(result, Is.Empty); + } +} diff --git a/dotnet/test/common/CustomDriverConfigs/StableChannelChromeDriver.cs b/dotnet/test/common/CustomDriverConfigs/StableChannelChromeDriver.cs index 718ae0f6c6473..3c336df0166b1 100644 --- a/dotnet/test/common/CustomDriverConfigs/StableChannelChromeDriver.cs +++ b/dotnet/test/common/CustomDriverConfigs/StableChannelChromeDriver.cs @@ -20,7 +20,7 @@ public StableChannelChromeDriver(ChromeDriverService service, ChromeOptions opti public static ChromeOptions DefaultOptions { - get { return new ChromeOptions() { BrowserVersion = "129" }; } + get { return new ChromeOptions() { BrowserVersion = "130" }; } } } } diff --git a/dotnet/test/common/DevTools/DevToolsConsoleTest.cs b/dotnet/test/common/DevTools/DevToolsConsoleTest.cs index a2437fb1c37cc..b3d6c959512ff 100644 --- a/dotnet/test/common/DevTools/DevToolsConsoleTest.cs +++ b/dotnet/test/common/DevTools/DevToolsConsoleTest.cs @@ -6,7 +6,7 @@ namespace OpenQA.Selenium.DevTools { - using CurrentCdpVersion = V129; + using CurrentCdpVersion = V130; [TestFixture] public class DevToolsConsoleTest : DevToolsTestFixture diff --git a/dotnet/test/common/DevTools/DevToolsLogTest.cs b/dotnet/test/common/DevTools/DevToolsLogTest.cs index 096eac7fc662e..e1f05af80409a 100644 --- a/dotnet/test/common/DevTools/DevToolsLogTest.cs +++ b/dotnet/test/common/DevTools/DevToolsLogTest.cs @@ -6,7 +6,7 @@ namespace OpenQA.Selenium.DevTools { - using CurrentCdpVersion = V129; + using CurrentCdpVersion = V130; [TestFixture] public class DevToolsLogTest : DevToolsTestFixture diff --git a/dotnet/test/common/DevTools/DevToolsNetworkTest.cs b/dotnet/test/common/DevTools/DevToolsNetworkTest.cs index 81436eb26178c..d9fb0269ab681 100644 --- a/dotnet/test/common/DevTools/DevToolsNetworkTest.cs +++ b/dotnet/test/common/DevTools/DevToolsNetworkTest.cs @@ -6,7 +6,7 @@ namespace OpenQA.Selenium.DevTools { - using CurrentCdpVersion = V129; + using CurrentCdpVersion = V130; [TestFixture] public class DevToolsNetworkTest : DevToolsTestFixture diff --git a/dotnet/test/common/DevTools/DevToolsPerformanceTest.cs b/dotnet/test/common/DevTools/DevToolsPerformanceTest.cs index b23ef6a6825b6..6b7ea7954432e 100644 --- a/dotnet/test/common/DevTools/DevToolsPerformanceTest.cs +++ b/dotnet/test/common/DevTools/DevToolsPerformanceTest.cs @@ -3,7 +3,7 @@ namespace OpenQA.Selenium.DevTools { - using CurrentCdpVersion = V129; + using CurrentCdpVersion = V130; [TestFixture] public class DevToolsPerformanceTest : DevToolsTestFixture diff --git a/dotnet/test/common/DevTools/DevToolsProfilerTest.cs b/dotnet/test/common/DevTools/DevToolsProfilerTest.cs index fa90ffb9761d2..fed67173d3c66 100644 --- a/dotnet/test/common/DevTools/DevToolsProfilerTest.cs +++ b/dotnet/test/common/DevTools/DevToolsProfilerTest.cs @@ -5,7 +5,7 @@ namespace OpenQA.Selenium.DevTools { - using CurrentCdpVersion = V129; + using CurrentCdpVersion = V130; [TestFixture] public class DevToolsProfilerTest : DevToolsTestFixture diff --git a/dotnet/test/common/DevTools/DevToolsSecurityTest.cs b/dotnet/test/common/DevTools/DevToolsSecurityTest.cs index 1cde50c30cc0b..ede5071b6621b 100644 --- a/dotnet/test/common/DevTools/DevToolsSecurityTest.cs +++ b/dotnet/test/common/DevTools/DevToolsSecurityTest.cs @@ -6,7 +6,7 @@ namespace OpenQA.Selenium.DevTools { - using CurrentCdpVersion = V129; + using CurrentCdpVersion = V130; [TestFixture] public class DevToolsSecurityTest : DevToolsTestFixture diff --git a/dotnet/test/common/DevTools/DevToolsTabsTest.cs b/dotnet/test/common/DevTools/DevToolsTabsTest.cs index bfac7ab874486..d71c9ed596abb 100644 --- a/dotnet/test/common/DevTools/DevToolsTabsTest.cs +++ b/dotnet/test/common/DevTools/DevToolsTabsTest.cs @@ -3,7 +3,7 @@ namespace OpenQA.Selenium.DevTools { - using CurrentCdpVersion = V129; + using CurrentCdpVersion = V130; [TestFixture] public class DevToolsTabsTest : DevToolsTestFixture diff --git a/dotnet/test/common/DevTools/DevToolsTargetTest.cs b/dotnet/test/common/DevTools/DevToolsTargetTest.cs index a7bb2a98d2bfb..fa31474612254 100644 --- a/dotnet/test/common/DevTools/DevToolsTargetTest.cs +++ b/dotnet/test/common/DevTools/DevToolsTargetTest.cs @@ -6,12 +6,12 @@ namespace OpenQA.Selenium.DevTools { - using CurrentCdpVersion = V129; + using CurrentCdpVersion = V130; [TestFixture] public class DevToolsTargetTest : DevToolsTestFixture { - private int id = 129; + private int id = 130; [Test] [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] diff --git a/dotnet/test/common/Environment/DriverFactory.cs b/dotnet/test/common/Environment/DriverFactory.cs index 8ce615235dc7d..5708ebda6495d 100644 --- a/dotnet/test/common/Environment/DriverFactory.cs +++ b/dotnet/test/common/Environment/DriverFactory.cs @@ -67,12 +67,6 @@ public IWebDriver CreateDriverWithOptions(Type driverType, DriverOptions driverO { browser = Browser.Chrome; options = GetDriverOptions(driverType, driverOptions); - // Disabling this since we do not have any BiDi tests currently. - //options.UseWebSocketUrl = true; - - // If BiDi is enabled above then the undhandler prompt behaviour needs to set accordingly. - // Reasoning : https://github.com/SeleniumHQ/selenium/pull/14429#issuecomment-2311614822 - //options.UnhandledPromptBehavior = UnhandledPromptBehavior.Ignore; var chromeOptions = (ChromeOptions)options; chromeOptions.AddArguments("--no-sandbox", "--disable-dev-shm-usage"); @@ -188,6 +182,8 @@ protected void OnDriverLaunching(DriverService service, DriverOptions options) options.ScriptTimeout = overriddenOptions.ScriptTimeout; options.PageLoadTimeout = overriddenOptions.PageLoadTimeout; options.ImplicitWaitTimeout = overriddenOptions.ImplicitWaitTimeout; + + options.UseWebSocketUrl = overriddenOptions.UseWebSocketUrl; } return options; diff --git a/java/CHANGELOG b/java/CHANGELOG index 9daf9573fc362..98cf2a20a00a6 100644 --- a/java/CHANGELOG +++ b/java/CHANGELOG @@ -1,3 +1,21 @@ +v4.26.0 +====== +* Add CDP for Chrome 130 and remove 127 +* Add PAC proxy url to arguments for Selenium Manager (#14506) +* Prevent closing the stdin, stdout, stderr streams (#14569) +* Increasing of properties scope for better Appium compatibility (#14183) +* Fix decoding of line endings (#14539) +* Fix SpotBugs findings in `ChromiumDriver` and `PortProber` (#14589) +* Fix `v*Network.java` conditions (#14585) +* [grid] Enable the httpclient to perform async requests (#14409) +* [grid] Limit the number of websocket connections per session (#14410) +* [grid] Improvement for Node handling (#14628) +* [grid] Add node sessionTimeout to Grid status (#14582) +* [grid] Capability se:vncEnabled value based on list of vnc-env-var (#14584) +* [grid] UI Sessions list view sort Duration in ascending by default (#14599) +* [grid] UI Liveview disconnect noVNC websocket when closing dialog (#14598) +* [grid] UI Overview is able to sort Nodes capabilities (#14571) + v4.25.0 ====== * Add CDP for Chrome 129 and remove 126 diff --git a/java/maven_install.json b/java/maven_install.json index 04f5468f594a2..9034f3dee04b2 100644 --- a/java/maven_install.json +++ b/java/maven_install.json @@ -1,13 +1,13 @@ { "__AUTOGENERATED_FILE_DO_NOT_MODIFY_THIS_FILE_MANUALLY": "THERE_IS_NO_DATA_ONLY_ZUUL", - "__INPUT_ARTIFACTS_HASH": 1327312787, - "__RESOLVED_ARTIFACTS_HASH": -2038995232, + "__INPUT_ARTIFACTS_HASH": 282748108, + "__RESOLVED_ARTIFACTS_HASH": 1016391538, "conflict_resolution": { "com.google.code.gson:gson:2.8.9": "com.google.code.gson:gson:2.11.0", "com.google.errorprone:error_prone_annotations:2.3.2": "com.google.errorprone:error_prone_annotations:2.28.0", "com.google.guava:guava:31.1-jre": "com.google.guava:guava:33.3.1-jre", "com.google.j2objc:j2objc-annotations:1.3": "com.google.j2objc:j2objc-annotations:3.0.0", - "org.mockito:mockito-core:4.3.1": "org.mockito:mockito-core:5.13.0" + "org.mockito:mockito-core:4.3.1": "org.mockito:mockito-core:5.14.2" }, "artifacts": { "com.beust:jcommander": { @@ -40,31 +40,31 @@ }, "com.fasterxml.jackson.core:jackson-annotations": { "shasums": { - "jar": "873a606e23507969f9bbbea939d5e19274a88775ea5a169ba7e2d795aa5156e1", - "sources": "c647697c578c4126e0ccae72924b641a824dddfce6db9935e4a4daefd59d06f2" + "jar": "a09367d2eeb526873abf737ff754c5085f82073a382ee72c3bbe15412217671f", + "sources": "506c61d1efe6cf7aff5ec13a4b87532185e7a872dc2ffea66dc57bdcc1108f15" }, - "version": "2.17.2" + "version": "2.18.0" }, "com.fasterxml.jackson.core:jackson-core": { "shasums": { - "jar": "721a189241dab0525d9e858e5cb604d3ecc0ede081e2de77d6f34fa5779a5b46", - "sources": "abd7bfe1f3ae15c6e56409555954a10424e5bfc0ac3119449647347bded0032b" + "jar": "215bbd7c8fd65be504cb92ff3aa1c4b790fc7b14cca72f4546aac4143c101bb5", + "sources": "afb36a31e0038a31d79e30719315ad5c7a1e140443ef400108d104d72c943162" }, - "version": "2.17.2" + "version": "2.18.0" }, "com.fasterxml.jackson.core:jackson-databind": { "shasums": { - "jar": "c04993f33c0f845342653784f14f38373d005280e6359db5f808701cfae73c0c", - "sources": "09fb0d67d3c9fc3c03adef0ca87df3dee7e7a7db8ffd331dcdf09f62b6b66342" + "jar": "2bf1927b7f3224683ed0157a1ec3b0ede75179da3e597d78c572d56ed00f9f3c", + "sources": "0531788c8193e81e40e16ff7c16401d7c5cb629ed493bb13d091d980bc425123" }, - "version": "2.17.2" + "version": "2.18.0" }, "com.fasterxml.jackson.dataformat:jackson-dataformat-yaml": { "shasums": { - "jar": "941bcd8b1381bb3b0d726fab41624fa8ece0ee7b6cf2860ad95e8157ce673376", - "sources": "d0938b1f8df14edff08e3ea3e6f65658f6d4506e0e13ea13a6f3f59941ba0a34" + "jar": "1e14646be85443e9be00c18fe7d67b71953edaed97ce0b30ce822279933aeaca", + "sources": "6fb2e6b2b39e2a93787f5d8ce3ea851623af6f1ecdbce2a724732936ade12fd4" }, - "version": "2.17.2" + "version": "2.18.0" }, "com.github.javaparser:javaparser-core": { "shasums": { @@ -145,10 +145,10 @@ }, "com.google.googlejavaformat:google-java-format": { "shasums": { - "jar": "f9c5f181faee5c7b380feb96c1a94bec0c55859baeb2d14e9a47d92d40bef021", - "sources": "d3a4c8d7a2349396342033f2a9ee0632ae840523c84f67f63503d01d9cd841ac" + "jar": "9a4e0b9f4ec4d71a8a1d3641fd481118100fda2eeab712dbdfd4b2a06e9de4ce", + "sources": "d4dfd144c7dceca98cfa479591398160558c0311750eed069ebd796cda420d55" }, - "version": "1.23.0" + "version": "1.24.0" }, "com.google.guava:failureaccess": { "shasums": { @@ -243,17 +243,17 @@ }, "io.grpc:grpc-api": { "shasums": { - "jar": "8fadb1f4f0a18971c082497f34cbb78a51897ca8af4b212aa2a99c7de9ad995c", - "sources": "6d0df2072702a1badfaaab3cce14f2629d4eaec85cf696347561bec19736ce8c" + "jar": "b5120a11da5ce5ddfab019bbb69f5868529c9b5def1ba5b283251cc95fb3ba91", + "sources": "3e4b31496f2c8b7cd51b425af767c72d44b38fdbb56a6e8c247acb8a721c4e8c" }, - "version": "1.66.0" + "version": "1.68.0" }, "io.grpc:grpc-context": { "shasums": { - "jar": "7b7521aa2116014d08dc08825e13d70eac8eb646d09dd44980b6f4d1883e6713", - "sources": "c4638347d6d0964eb7a3987cd9d943554cbdf5e334b26f6d119dfc0e85fb1899" + "jar": "45f85a394466f963f1f7a5c5555e6dda35efd05ce1c687203a217d7048f6f089", + "sources": "31d4fc1054b5c0bc75924e82ca425dcf624f895e7525da900b94cfa87a2bea53" }, - "version": "1.66.0" + "version": "1.68.0" }, "io.lettuce:lettuce-core": { "shasums": { @@ -264,94 +264,94 @@ }, "io.netty:netty-buffer": { "shasums": { - "jar": "a85c198180a8de997e8f64a62e054946a39af0708466c1bd67747d393d2feee1", - "sources": "b21b7a03dd5ab0af84def551fcbaf26d61f2e9e8a0b9581b067a7722f1e5322c" + "jar": "436ea25725d92c1f590a857d46dc1c8f5c54d7f2775984db2655e5e3bc0d97c9", + "sources": "62bec4230845ec978832f602b9a118b57955cf816da07b99fccd33430d5da3a8" }, - "version": "4.1.113.Final" + "version": "4.1.114.Final" }, "io.netty:netty-codec": { "shasums": { - "jar": "c7669cbeda2b5c6284627b7ffafd6f1014a96639013b26ebb9d6bcb828f76542", - "sources": "e54247ed6809b0368fe21dcd2ff0f15f0dffc427e4691f82db9eec0ee0541006" + "jar": "71d145491456b53212e38e24787bbbed0c77d850f7352e24e535d26a6eea913b", + "sources": "f0bc1dcab04f9c9fb9fb5450eb1636c7c99b67a772ceb7896e80094400e8c74c" }, - "version": "4.1.113.Final" + "version": "4.1.114.Final" }, "io.netty:netty-codec-dns": { "shasums": { - "jar": "8939e59890dba2b72f720811c15034178449f2ce0ff3daf7942f0fb488d7ee6b", - "sources": "9e07213699ba113086cc8e059cdfb9e3acc68f461d8bc28446e97d68a09073be" + "jar": "65d9690298403ada696a7cbf9ebab2106e9d6a9056ae51d5fda7c4c553d9a21a", + "sources": "edf01b849d83b8f6bbd754ed324e418a2c8cabf8659fa5316b285580307b85dd" }, - "version": "4.1.112.Final" + "version": "4.1.113.Final" }, "io.netty:netty-codec-http": { "shasums": { - "jar": "bd5ebc6435d78d6fc96b36545d7e5ef00ec1045fd87ef367a9bc951c76400490", - "sources": "233477291b8fc8defa9b93cbc92021e6ceeca1dd5ef1e85701cb1810b5a231f3" + "jar": "56150ce900f6d931fce37a7fb05d7d75478d6d0b8b556a21781972eb9c3ed7df", + "sources": "69a06bd827300e88736660df76fc1d590379f2a161ca1fbc6aaaf8e1c9554ada" }, - "version": "4.1.113.Final" + "version": "4.1.114.Final" }, "io.netty:netty-codec-http2": { "shasums": { - "jar": "15e37fe28b31d92b77edb804ffe6d194bce330be194b77b3911afad03b21a07c", - "sources": "7e2c9bc7e789dc06f3cafdd7c0a8c882e3ac0a5810d4fb053a54396b8eeb960b" + "jar": "e3e45427a46a8d5b03307a5bcd2eab3706b8b8d903c6a056fa3d1c9bf9738f24", + "sources": "e8e3f6c709ddcef83bfff99b144bc532e235dc8bdd685621179a7a08ddb763bd" }, - "version": "4.1.113.Final" + "version": "4.1.114.Final" }, "io.netty:netty-codec-socks": { "shasums": { - "jar": "d871bfd5d9c648b640fa26fbe4bab82d05583f6f359f5428995cd4b91d3407cb", - "sources": "d8beecbd55020e25b88840fe72b9a125168416b4f1db9f3c628a5fa0bf4ac8a3" + "jar": "2e48cb5af7cae82acbb8dcb7b0c9d67b9dddff3d6dd262cb8911fd4b5fd62ee2", + "sources": "2d90b45bc6f02c2c708c1773aae010b344b260bb4b033fbd01d054aed56f9c9e" }, - "version": "4.1.113.Final" + "version": "4.1.114.Final" }, "io.netty:netty-common": { "shasums": { - "jar": "c0fb22d47111cb06aac2af67fe55e2e216a49fd00e767f4acb7488b280f8c327", - "sources": "ed34484d83860c3721c709bb38abd2a1f91c8edea18d59fd4e89da1e96b4d911" + "jar": "d6b053b3b27cc568207f254902dcb6f95dd238c1b9d55ef719d2c4f8eb476223", + "sources": "fac8937c115808c600fcae35964e54ae4eef344b81c22f8de8913ca95ddbcebf" }, - "version": "4.1.113.Final" + "version": "4.1.114.Final" }, "io.netty:netty-handler": { "shasums": { - "jar": "7a583c4fe5880504d1257a4a6bfe6a635cd34ffe18aab0e4caabbf17d104f172", - "sources": "968ead1589c79ab5488b031f4564ee4f2cb0d0c17e9cf9b42ad826ed74e85293" + "jar": "57be25ec6c8fa7052fe90119373d8bc979cd37fa0070a135c0e69f5f8e0ddad0", + "sources": "f07c13c7bd69f481136983bdf722fe64be29405065842010669d443455379c24" }, - "version": "4.1.113.Final" + "version": "4.1.114.Final" }, "io.netty:netty-handler-proxy": { "shasums": { - "jar": "78e7890ca04255785fb441c4ba7bc4954536b4aa58622fa86f6dc8b6004cca59", - "sources": "ce3511277296605cb26a4e963655bdcd7bbf222c238ec868cb7922022a93291a" + "jar": "35555b41624c8384de773bc3d17eb2447f5449842119db0435a366372aceefd1", + "sources": "af00561a494ad88a960b02ffae8153bd7c04cd2aa87183e884ad146fee67cda9" }, - "version": "4.1.113.Final" + "version": "4.1.114.Final" }, "io.netty:netty-resolver": { "shasums": { - "jar": "b2c110a547e7dc6e2b27017ea4ef98416d7d832d2cf40625d0273b90e61df8ab", - "sources": "2bcdae61be836aac497ec1d1a232f22a541a8d51436d20dcb12413019029ab63" + "jar": "19661e7f1dbdee97fe99a227fbed0696d29c3cdf3f8f2d9839a790695c2bf0ac", + "sources": "83ddf3e14d989e90d28811e7c2697dfef35061fe63b8939ddaf42e5c91235681" }, - "version": "4.1.113.Final" + "version": "4.1.114.Final" }, "io.netty:netty-resolver-dns": { "shasums": { - "jar": "ad918df020ba6e10aeecc0b47133754d0c2616aa44d414bcb876d61bd704e56d", - "sources": "bfcfdcda25dcf603044c2923a418cf357d205f480161c7ff2c7f8edfcd307d4c" + "jar": "65c3bd4569bbe7d3d9b7b9991ac5fd2b1336d6f7a19e76fc60aa23a9bfe3b8f9", + "sources": "cfb42f6612adce6345ee5eca93fe4f5bf6540314fd3c2b8c561c255ac3299d53" }, - "version": "4.1.112.Final" + "version": "4.1.113.Final" }, "io.netty:netty-transport": { "shasums": { - "jar": "cb8b97ff77d7c5f1c591c84d2dee3389a0eaa63a3137b7b8c0c64e1dbada6688", - "sources": "36b22629d6dae03c6358fcef069471fd339fd66002094c5b22eaf2dfe90bb529" + "jar": "2a8609fe6a8b4c9d5965c6b901777b4bd0b26600647ee2aa7d4d93f4d5c780de", + "sources": "dd4923fb040730ea5ddb4cb66a0e236ffe75385a201badd7902a99e5fad81ee4" }, - "version": "4.1.113.Final" + "version": "4.1.114.Final" }, "io.netty:netty-transport-native-unix-common": { "shasums": { - "jar": "804640095390c1284a1ad537207c6d5b391cfb798a7ef976e5b238fcd9c08ea3", - "sources": "6c1eefb4fec3fe864cee47dbabd7b7ea4dbf693efaf67e0070f8d71447f1fadc" + "jar": "fd64c07c9e068f80dc271f6277278246328a171be669abdfe0bc8b2226d980de", + "sources": "84e5081550855aa7567a215b94fdc6cd7e8c62dabc9503fc2c3c72e0c3855a48" }, - "version": "4.1.113.Final" + "version": "4.1.114.Final" }, "io.opentelemetry.semconv:opentelemetry-semconv": { "shasums": { @@ -362,87 +362,87 @@ }, "io.opentelemetry:opentelemetry-api": { "shasums": { - "jar": "6b0f9d067260ea3ed6c3960352b80800b993cb3962fa6fb1b6383cd04c3c0874", - "sources": "0c3c8c37171fa4eb7e2201cb575fee8ae5eb681890b849e3ef42c7d793eec841" + "jar": "4d08915aa1590bd64b3fced39bae06489e3f597c4bf7fa12f413e13c5a98be39", + "sources": "e8d0fbee6a7a19408e776bda9caecb2a4adeedf64cc196cfefd977837eb27300" }, - "version": "1.42.1" + "version": "1.43.0" }, "io.opentelemetry:opentelemetry-api-incubator": { "shasums": { - "jar": "2d5f478fe5971dc6cc454b483f84151280559f1e1a4b8dabea346fd425b6ad47", - "sources": "b924e38a40889978363ad07385d86e67a1112df4e5118578dd1c088d1ef110c3" + "jar": "a64d48638cdd4324e5ffab80c85784173f8c5068f059c07d8380f428d7c39ec8", + "sources": "871b4ef83803384b4a4ce794ea7b2384e0df70c8a15dc48afcf318525d7547f0" }, - "version": "1.42.1-alpha" + "version": "1.43.0-alpha" }, "io.opentelemetry:opentelemetry-context": { "shasums": { - "jar": "fc8f47bc94bec89a3dbdbcf631470fb7fd7d3e628b10d43bc376f17ebde4b405", - "sources": "143f5c77ced023235554da06e4c39b732d995f9599bf518bf4dae4a1bc9ae0bc" + "jar": "83d54bed8a7aa74a8648d43974c743f470c9ceab3dea57f26a938667a2dc0160", + "sources": "6cbaf1ff211bd61fd5ac2dd5762e18db56913035c43c963c3863eeff2bb25888" }, - "version": "1.42.1" + "version": "1.43.0" }, "io.opentelemetry:opentelemetry-exporter-logging": { "shasums": { - "jar": "f4e85c2756ff27accc1e90f221465149c3a0528d286701ed4aec70794daa72b3", - "sources": "6a71e942cd904192c81b1dc31f745439ad3b32f44713d56ea3286fae977932d1" + "jar": "68dff3d10b0875db8fa4c137f3e1f42f3fde8470f87463feae9c10acdcef7e32", + "sources": "652b5381cdba31eff8b8d402a296b03bdf49da423db0135251aee23794d5903d" }, - "version": "1.42.1" + "version": "1.43.0" }, "io.opentelemetry:opentelemetry-sdk": { "shasums": { - "jar": "df28b75c2df629c8971fd4afb59036d4861dc96789e6760a54a4266499ced5fb", - "sources": "2071f11cdc2caf4814b3ad60d2b49e578cee4d67676c693ff9d646e1073e07cd" + "jar": "99fd821997190e9fa1a6f6820b8cedd8e38297ab727b3b98db4d86ab518c0155", + "sources": "d798a21f2338674cd9dc7249d67963f7328b9a0a702c91f65c9d20b4b1eff664" }, - "version": "1.42.1" + "version": "1.43.0" }, "io.opentelemetry:opentelemetry-sdk-common": { "shasums": { - "jar": "0cb2f9e93291ccfe7099ed424b7616e7e80ee51fdbbff99d2b2365f52428b179", - "sources": "62af024af0f5f13ee3f9640356073bf6dd819b47548f6fc2ceda90d34bc8b5e2" + "jar": "8d0cc91322e85ea6f71dbf5d35f829808958fd0427e0c43aa3fdd2e94e56a48b", + "sources": "e240713669cc161a6898c9c405fdf0903f69ff35abde95827d1f6660df58c0cc" }, - "version": "1.42.1" + "version": "1.43.0" }, "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure": { "shasums": { - "jar": "ce1186edb83c68e5fb91877cf018a75703153befa36ce6628b11f603f681e00e", - "sources": "787e25715d13f38d02e2f77d741b0df6643fc7f3800646d6ee32671e3846db5e" + "jar": "c731376048e1b2c89cf184227abfdb2e5baafb687fa4091901e97b4f3e8c6985", + "sources": "0cc04966769b3b65c8df3769ef90908eae77aa16fb6f82895d4ff80a4a198d75" }, - "version": "1.42.1" + "version": "1.43.0" }, "io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi": { "shasums": { - "jar": "fe095e16871b942cae7fed6e0b3bbff462111fe62fe31ccd34d7542f8ebcfe90", - "sources": "4dd4752c749d50487ab0e5753dfbf9289a3efb5768c73fe5e575239513338a80" + "jar": "ae0b08fbe8b3146ff2a8e14cc48af418d6f8f808527d00406b0778748cd07f24", + "sources": "c92792a6f691b68f513ae3b9d4a1bc97299881b08399f3fb1900e947e5434dca" }, - "version": "1.42.1" + "version": "1.43.0" }, "io.opentelemetry:opentelemetry-sdk-logs": { "shasums": { - "jar": "e8229fe1305ad76a879d2dcccffb189308423fc45602bd20715dae9e52862bc0", - "sources": "c632f4d62c801d516e60d4899679de715d2897d4fffd7c737e26fba7ee60a452" + "jar": "5465297bac529a32be7c3e37bc854f1f683df967551cc092397deaa2596f4462", + "sources": "234fb21e31806e054c53044cf97d82786390b3634cc94fd42b4fe5c12d86a553" }, - "version": "1.42.1" + "version": "1.43.0" }, "io.opentelemetry:opentelemetry-sdk-metrics": { "shasums": { - "jar": "0144c6f2845c25baab653764b365d178f589cd9d427d8a30ea06dafdf75576c5", - "sources": "3a8d6977adc1b792cc00fdee48701efe4e3c5a7609aa2e4a62d46654ed530a56" + "jar": "b3cd4906157c559005d6890441d367730c337ad4d860adeee7ceda1106cfc245", + "sources": "d7523f6dbeb161fd4f0dabf1c6e414ab3eac153479acb95547155f5b16f8e557" }, - "version": "1.42.1" + "version": "1.43.0" }, "io.opentelemetry:opentelemetry-sdk-testing": { "shasums": { - "jar": "402267cbc8fc93bbea0f85212e95ac7d3cf7e29c9f26dd88e9c43f7e1ef45280", - "sources": "7caeaa8d20e562450ef76672530af6c9dd49e3003071e5e8530d9bb86360aa42" + "jar": "37512f646f55312cc0a646a0b508495d993d818aae2df5403497c5a096cd5571", + "sources": "bf62824394d155329ce095e4ad64cb6d3b2daf3f7a4b5c15dea21c5e3196ba5b" }, - "version": "1.42.1" + "version": "1.43.0" }, "io.opentelemetry:opentelemetry-sdk-trace": { "shasums": { - "jar": "23a4ab8ed8cfb32cc3ef3a2bc921eb8e9f2c6c73e0bf184061c68b4fc2c98b02", - "sources": "bb7b29c5d7587e1ff0ce34b8162ad0c852eb000f27932995390588be8dcc7719" + "jar": "7e28072cf33eaa7d5e5f3ab2f8b2e51e54d24e0fe3dae3482769b0f6ff0dfcfc", + "sources": "b75d547249b04ad5163c17253018159f3f40a99d6d89719a82d11eb3d64588f1" }, - "version": "1.42.1" + "version": "1.43.0" }, "io.ous:jtoml": { "shasums": { @@ -495,17 +495,17 @@ }, "net.bytebuddy:byte-buddy": { "shasums": { - "jar": "cc5f178f37ef83339b7ec93e8d0bed6b0730871cdb39c663527ddeae4a54a825", - "sources": "fca376d0298b5528dda33b06378a8820c6e9029e4ab19c89b64d2344cb4ae700" + "jar": "7f77ee7ae0a6f420218546424a92fc6c964ed5788b21a2559d6be177c5e1a718", + "sources": "d19281fa34e46008ff096ec955659f94c81df9a3301109e33503ac15f9e1c44d" }, - "version": "1.15.1" + "version": "1.15.7" }, "net.bytebuddy:byte-buddy-agent": { "shasums": { - "jar": "3399a0fdf7ba3f1386ebf831a706037428f1b1af81d653c25cf8a8fde2e4d2ea", - "sources": "e1851c192c949dac8e84e935ebec97e95a96af0c51c84451a9fa4667ef047188" + "jar": "1d76defd159a564b9cb7a968d0dea27367b8b70ebde75a968e7ef1921bc75ee4", + "sources": "8a7e537a5c2a932a6d73dcec9aa8d5388d33ab3aa91ea410aeea5228dcfe9745" }, - "version": "1.15.0" + "version": "1.15.4" }, "net.sf.saxon:Saxon-HE": { "shasums": { @@ -565,17 +565,17 @@ }, "org.apache.logging.log4j:log4j-api": { "shasums": { - "jar": "de99b52578c62ee0125dd345e7121502facfe29294314ae684a4a12314c4e55a", - "sources": "1678a06935bdb4fc9f43af6ada2b495e284517c7f5a44d9a653cddb395c8b4e1" + "jar": "6e77bb229fc8dcaf09038beeb5e9030b22e9e01b51b458b0183ce669ebcc92ef", + "sources": "e01afccb47c9340abba621f1cd93e05a092525614071a446c3958f2cd9f48bf6" }, - "version": "2.24.0" + "version": "2.24.1" }, "org.apache.logging.log4j:log4j-core": { "shasums": { - "jar": "3f5b93c80f0f3d2e8cfb166a7d64ec589f8c9326fa0d7c41d74d63b28f6fd62e", - "sources": "48f975893afb7ba045583c9c4de965809b1ddd3a2948cf16ac003a86ee4b2d56" + "jar": "00bcf388472ca80a687014181763b66d777177f22cbbf179fd60e1b1ac9bc9b0", + "sources": "b5b4eafe913e457160a3c19773c7f59e132a62b1c74ab9fa744ebc6f9ba9bc9a" }, - "version": "2.24.0" + "version": "2.24.1" }, "org.apiguardian:apiguardian-api": { "shasums": { @@ -642,10 +642,10 @@ }, "org.htmlunit:htmlunit-core-js": { "shasums": { - "jar": "1acde070d8419a402da6fc30a16e5e348831ba6e323c31b2777e733593ead8c6", - "sources": "396abdec68dae0c18362e059bbc31fdd01f19d4865ca561d603ed13b84279e73" + "jar": "70c9a224a75670a6d135e65deac885462e34a6756559170527cffcd613f3ca1a", + "sources": "87645340ab720096be5267b9ac820d60d968f690c024e8190be2942afe027309" }, - "version": "4.4.0" + "version": "4.5.0" }, "org.jodd:jodd-util": { "shasums": { @@ -663,59 +663,59 @@ }, "org.junit.jupiter:junit-jupiter-api": { "shasums": { - "jar": "42aa202fc862f76cc5af65b47b1c0b1961cdd79cd2216405a6dfa2bd20b20974", - "sources": "cbcd62ebf6d19b118eb69055ccf3cdedbdb349f42f9721446dd6958b19cc8c6a" + "jar": "5d8147a60f49453973e250ed68701b7ff055964fe2462fc2cb1ec1d6d44889ba", + "sources": "7f6a333b8c4e5c2d29c76c52883dfe2484145b8b2fc20346f8880b9e087f6336" }, - "version": "5.11.0" + "version": "5.11.3" }, "org.junit.jupiter:junit-jupiter-engine": { "shasums": { - "jar": "7012423383d0c79d0347c5cf2bd1996c30a12240fb729e0cdfa954852ec693cc", - "sources": "ab10b3e1cc9f694bbea76a9b2ca6f21ccaa4b2bab45a520673ca9b03ce2f5bb4" + "jar": "e62420c99f7c0d59a2159a2ef63e61877e9c80bd722c03ca8bf3bdcea050a589", + "sources": "b2343451cf7f9cd0044b4a614adca1dd88f3a9265256c88a4a6f3b68e65075c8" }, - "version": "5.11.0" + "version": "5.11.3" }, "org.junit.jupiter:junit-jupiter-params": { "shasums": { - "jar": "92ccae2d72e8cc7ac4d3a912fd1a8fecc5e3040a62ac6c667a07a6f55b8023eb", - "sources": "db6d5e95e5909e64ec4cb4b72d2360b0264ed9d07aa50d86a9c525d09907ce9e" + "jar": "0f798ebec744c4e6605fd4f2072f41a8e989e2d469e21db5aa67cf799c0b51ec", + "sources": "073ce1b0f7fa3ee1e89e301e5f92078058ce7702ba662652cc3932d9573137bc" }, - "version": "5.11.0" + "version": "5.11.3" }, "org.junit.platform:junit-platform-commons": { "shasums": { - "jar": "609333a4545f9018eb0c59071efd30663a9e9fdce528121b65a04c27e5fc26a7", - "sources": "47be9d7484beba0cd2cac767a2fb4cfbaafc4f386e916a843dfe3dd1c4eff5c5" + "jar": "be262964b0b6b48de977c61d4f931df8cf61e80e750cc3f3a0a39cdd21c1008c", + "sources": "a2f0c562a7fe4066044d93bb20ce527b4669c1ddaefca2244a53356db747c5e2" }, - "version": "1.11.0" + "version": "1.11.3" }, "org.junit.platform:junit-platform-engine": { "shasums": { - "jar": "a7e67279c651c516949512b506916475a6d9e284cd4f4c30d029b4ad73a944d8", - "sources": "46109b01147fb8435c5a16c1d7e71d3e76f4b428e50bacf0763aa40814c17c39" + "jar": "0043f72f611664735da8dc9a308bf12ecd2236b05339351c4741edb4d8fab0da", + "sources": "96acc7bc533f52421a149faff2e3e9d6958d167f4200da74815b208f9846a615" }, - "version": "1.11.0" + "version": "1.11.3" }, "org.junit.platform:junit-platform-launcher": { "shasums": { - "jar": "a44535e639814236844e2247204f89247d13af0cebdea53a42314100dfde19ce", + "jar": "b4727459201b0011beb0742bd807421a1fc8426b116193031ed87825bc2d4f04", "sources": "e560e5e7bc6eed184774f75a7b36b824e20ec818be4b991213c0d31aff4260c5" }, - "version": "1.11.0" + "version": "1.11.3" }, "org.junit.platform:junit-platform-reporting": { "shasums": { - "jar": "e46b5a6420dfd25d7c18ab0c0d1cc0616fc5e057949bb877a847c8afbfa64cd6", + "jar": "b8e19dbebcae7d1ff30b9d767047fbf3694027c33dfa423b371693b7f6679ed1", "sources": "d19baa18b721ae6750004afdf63aefaf9d8ce2646752ec5c0346abe7d744086b" }, - "version": "1.11.0" + "version": "1.11.3" }, "org.mockito:mockito-core": { "shasums": { - "jar": "f8a6dad9511fbc809c493b1840414e42172335e414bdbabe643dd2d53dae9a7e", - "sources": "5bb0e8cdc11586a0305c3e2af7f1dacafaffc96df8226f83d099d0c9eeba95de" + "jar": "2296141c1e1f2e1ae35c08d36a9ab4563ecd66e03533fe82630a764e7aa49182", + "sources": "32f318184ab3795885743f23d8be0da7fe856e3e360b518083f8b366a44d2b33" }, - "version": "5.13.0" + "version": "5.14.2" }, "org.objenesis:objenesis": { "shasums": { @@ -775,10 +775,10 @@ }, "org.redisson:redisson": { "shasums": { - "jar": "7c031183fa0b3070467dad51131ab57b70dcef961156a68a949de432cc248d6b", - "sources": "d3aac121720386669d625b6b89601ece83ea2e7ab8d15540be5f6a59328d245b" + "jar": "c9840ce1cc8ccf03e95ac6901add9e6342711caf5a1c00f52687b960d979209b", + "sources": "a26f27c9386b38d72d539e21664bc331db2c5b014b925226cb2227c9625057ee" }, - "version": "3.36.0" + "version": "3.37.0" }, "org.slf4j:slf4j-api": { "shasums": { @@ -1208,8 +1208,8 @@ "com.fasterxml.jackson.core.exc", "com.fasterxml.jackson.core.filter", "com.fasterxml.jackson.core.format", + "com.fasterxml.jackson.core.internal.shaded.fdp.v2_18_0", "com.fasterxml.jackson.core.io", - "com.fasterxml.jackson.core.io.doubleparser", "com.fasterxml.jackson.core.io.schubfach", "com.fasterxml.jackson.core.json", "com.fasterxml.jackson.core.json.async", @@ -2614,7 +2614,6 @@ "org.htmlunit.corejs.javascript.debug", "org.htmlunit.corejs.javascript.jdk18", "org.htmlunit.corejs.javascript.json", - "org.htmlunit.corejs.javascript.optimizer", "org.htmlunit.corejs.javascript.regexp", "org.htmlunit.corejs.javascript.serialize", "org.htmlunit.corejs.javascript.tools", diff --git a/java/src/org/openqa/selenium/bidi/script/NodeProperties.java b/java/src/org/openqa/selenium/bidi/script/NodeProperties.java index b35c6b64299e9..a7243b21a526a 100644 --- a/java/src/org/openqa/selenium/bidi/script/NodeProperties.java +++ b/java/src/org/openqa/selenium/bidi/script/NodeProperties.java @@ -25,7 +25,7 @@ public class NodeProperties { private enum Mode { OPEN("open"), - CLOSE("close"); + CLOSED("closed"); private final String value; diff --git a/java/src/org/openqa/selenium/devtools/v127/BUILD.bazel b/java/src/org/openqa/selenium/devtools/v130/BUILD.bazel similarity index 98% rename from java/src/org/openqa/selenium/devtools/v127/BUILD.bazel rename to java/src/org/openqa/selenium/devtools/v130/BUILD.bazel index 3c97faa8a798a..169f88a358e6b 100644 --- a/java/src/org/openqa/selenium/devtools/v127/BUILD.bazel +++ b/java/src/org/openqa/selenium/devtools/v130/BUILD.bazel @@ -2,7 +2,7 @@ load("//common:defs.bzl", "copy_file") load("//java:defs.bzl", "java_export", "java_library") load("//java:version.bzl", "SE_VERSION") -cdp_version = "v127" +cdp_version = "v130" java_export( name = cdp_version, diff --git a/java/src/org/openqa/selenium/devtools/v127/v127CdpInfo.java b/java/src/org/openqa/selenium/devtools/v130/v130CdpInfo.java similarity index 86% rename from java/src/org/openqa/selenium/devtools/v127/v127CdpInfo.java rename to java/src/org/openqa/selenium/devtools/v130/v130CdpInfo.java index cef17b8d05d04..fe9227f90b64e 100644 --- a/java/src/org/openqa/selenium/devtools/v127/v127CdpInfo.java +++ b/java/src/org/openqa/selenium/devtools/v130/v130CdpInfo.java @@ -15,15 +15,15 @@ // specific language governing permissions and limitations // under the License. -package org.openqa.selenium.devtools.v127; +package org.openqa.selenium.devtools.v130; import com.google.auto.service.AutoService; import org.openqa.selenium.devtools.CdpInfo; @AutoService(CdpInfo.class) -public class v127CdpInfo extends CdpInfo { +public class v130CdpInfo extends CdpInfo { - public v127CdpInfo() { - super(127, v127Domains::new); + public v130CdpInfo() { + super(130, v130Domains::new); } } diff --git a/java/src/org/openqa/selenium/devtools/v127/v127Domains.java b/java/src/org/openqa/selenium/devtools/v130/v130Domains.java similarity index 77% rename from java/src/org/openqa/selenium/devtools/v127/v127Domains.java rename to java/src/org/openqa/selenium/devtools/v130/v130Domains.java index 5d2f4744ddb60..a6ab737b71a04 100644 --- a/java/src/org/openqa/selenium/devtools/v127/v127Domains.java +++ b/java/src/org/openqa/selenium/devtools/v130/v130Domains.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.openqa.selenium.devtools.v127; +package org.openqa.selenium.devtools.v130; import org.openqa.selenium.devtools.DevTools; import org.openqa.selenium.devtools.idealized.Domains; @@ -26,21 +26,21 @@ import org.openqa.selenium.devtools.idealized.target.Target; import org.openqa.selenium.internal.Require; -public class v127Domains implements Domains { +public class v130Domains implements Domains { - private final v127Javascript js; - private final v127Events events; - private final v127Log log; - private final v127Network network; - private final v127Target target; + private final v130Javascript js; + private final v130Events events; + private final v130Log log; + private final v130Network network; + private final v130Target target; - public v127Domains(DevTools devtools) { + public v130Domains(DevTools devtools) { Require.nonNull("DevTools", devtools); - events = new v127Events(devtools); - js = new v127Javascript(devtools); - log = new v127Log(); - network = new v127Network(devtools); - target = new v127Target(); + events = new v130Events(devtools); + js = new v130Javascript(devtools); + log = new v130Log(); + network = new v130Network(devtools); + target = new v130Target(); } @Override diff --git a/java/src/org/openqa/selenium/devtools/v127/v127Events.java b/java/src/org/openqa/selenium/devtools/v130/v130Events.java similarity index 86% rename from java/src/org/openqa/selenium/devtools/v127/v127Events.java rename to java/src/org/openqa/selenium/devtools/v130/v130Events.java index 45b630cab3a64..9d5329796952f 100644 --- a/java/src/org/openqa/selenium/devtools/v127/v127Events.java +++ b/java/src/org/openqa/selenium/devtools/v130/v130Events.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.openqa.selenium.devtools.v127; +package org.openqa.selenium.devtools.v130; import java.time.Instant; import java.util.List; @@ -28,15 +28,15 @@ import org.openqa.selenium.devtools.events.ConsoleEvent; import org.openqa.selenium.devtools.idealized.Events; import org.openqa.selenium.devtools.idealized.runtime.model.RemoteObject; -import org.openqa.selenium.devtools.v127.runtime.Runtime; -import org.openqa.selenium.devtools.v127.runtime.model.ConsoleAPICalled; -import org.openqa.selenium.devtools.v127.runtime.model.ExceptionDetails; -import org.openqa.selenium.devtools.v127.runtime.model.ExceptionThrown; -import org.openqa.selenium.devtools.v127.runtime.model.StackTrace; +import org.openqa.selenium.devtools.v130.runtime.Runtime; +import org.openqa.selenium.devtools.v130.runtime.model.ConsoleAPICalled; +import org.openqa.selenium.devtools.v130.runtime.model.ExceptionDetails; +import org.openqa.selenium.devtools.v130.runtime.model.ExceptionThrown; +import org.openqa.selenium.devtools.v130.runtime.model.StackTrace; -public class v127Events extends Events { +public class v130Events extends Events { - public v127Events(DevTools devtools) { + public v130Events(DevTools devtools) { super(devtools); } @@ -77,7 +77,7 @@ protected ConsoleEvent toConsoleEvent(ConsoleAPICalled event) { protected JavascriptException toJsException(ExceptionThrown event) { ExceptionDetails details = event.getExceptionDetails(); Optional maybeTrace = details.getStackTrace(); - Optional maybeException = + Optional maybeException = details.getException(); String message = diff --git a/java/src/org/openqa/selenium/devtools/v127/v127Javascript.java b/java/src/org/openqa/selenium/devtools/v130/v130Javascript.java similarity index 85% rename from java/src/org/openqa/selenium/devtools/v127/v127Javascript.java rename to java/src/org/openqa/selenium/devtools/v130/v130Javascript.java index 4a803b2fca88b..537e2eb735a0f 100644 --- a/java/src/org/openqa/selenium/devtools/v127/v127Javascript.java +++ b/java/src/org/openqa/selenium/devtools/v130/v130Javascript.java @@ -15,21 +15,21 @@ // specific language governing permissions and limitations // under the License. -package org.openqa.selenium.devtools.v127; +package org.openqa.selenium.devtools.v130; import java.util.Optional; import org.openqa.selenium.devtools.Command; import org.openqa.selenium.devtools.DevTools; import org.openqa.selenium.devtools.Event; import org.openqa.selenium.devtools.idealized.Javascript; -import org.openqa.selenium.devtools.v127.page.Page; -import org.openqa.selenium.devtools.v127.page.model.ScriptIdentifier; -import org.openqa.selenium.devtools.v127.runtime.Runtime; -import org.openqa.selenium.devtools.v127.runtime.model.BindingCalled; +import org.openqa.selenium.devtools.v130.page.Page; +import org.openqa.selenium.devtools.v130.page.model.ScriptIdentifier; +import org.openqa.selenium.devtools.v130.runtime.Runtime; +import org.openqa.selenium.devtools.v130.runtime.model.BindingCalled; -public class v127Javascript extends Javascript { +public class v130Javascript extends Javascript { - public v127Javascript(DevTools devtools) { + public v130Javascript(DevTools devtools) { super(devtools); } diff --git a/java/src/org/openqa/selenium/devtools/v127/v127Log.java b/java/src/org/openqa/selenium/devtools/v130/v130Log.java similarity index 89% rename from java/src/org/openqa/selenium/devtools/v127/v127Log.java rename to java/src/org/openqa/selenium/devtools/v130/v130Log.java index 8c5a1db4860a7..f665b4325a9c4 100644 --- a/java/src/org/openqa/selenium/devtools/v127/v127Log.java +++ b/java/src/org/openqa/selenium/devtools/v130/v130Log.java @@ -15,19 +15,19 @@ // specific language governing permissions and limitations // under the License. -package org.openqa.selenium.devtools.v127; +package org.openqa.selenium.devtools.v130; import java.util.function.Function; import java.util.logging.Level; import org.openqa.selenium.devtools.Command; import org.openqa.selenium.devtools.ConverterFunctions; import org.openqa.selenium.devtools.Event; -import org.openqa.selenium.devtools.v127.log.Log; -import org.openqa.selenium.devtools.v127.log.model.LogEntry; -import org.openqa.selenium.devtools.v127.runtime.model.Timestamp; +import org.openqa.selenium.devtools.v130.log.Log; +import org.openqa.selenium.devtools.v130.log.model.LogEntry; +import org.openqa.selenium.devtools.v130.runtime.model.Timestamp; import org.openqa.selenium.json.JsonInput; -public class v127Log implements org.openqa.selenium.devtools.idealized.log.Log { +public class v130Log implements org.openqa.selenium.devtools.idealized.log.Log { @Override public Command enable() { diff --git a/java/src/org/openqa/selenium/devtools/v127/v127Network.java b/java/src/org/openqa/selenium/devtools/v130/v130Network.java similarity index 92% rename from java/src/org/openqa/selenium/devtools/v127/v127Network.java rename to java/src/org/openqa/selenium/devtools/v130/v130Network.java index 769c646c40e7b..62ee7845f1451 100644 --- a/java/src/org/openqa/selenium/devtools/v127/v127Network.java +++ b/java/src/org/openqa/selenium/devtools/v130/v130Network.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.openqa.selenium.devtools.v127; +package org.openqa.selenium.devtools.v130; import static java.net.HttpURLConnection.HTTP_OK; @@ -30,35 +30,35 @@ import org.openqa.selenium.devtools.DevToolsException; import org.openqa.selenium.devtools.Event; import org.openqa.selenium.devtools.idealized.Network; -import org.openqa.selenium.devtools.v127.fetch.Fetch; -import org.openqa.selenium.devtools.v127.fetch.model.*; -import org.openqa.selenium.devtools.v127.network.model.Request; +import org.openqa.selenium.devtools.v130.fetch.Fetch; +import org.openqa.selenium.devtools.v130.fetch.model.*; +import org.openqa.selenium.devtools.v130.network.model.Request; import org.openqa.selenium.internal.Either; import org.openqa.selenium.remote.http.HttpRequest; import org.openqa.selenium.remote.http.HttpResponse; -public class v127Network extends Network { +public class v130Network extends Network { - private static final Logger LOG = Logger.getLogger(v127Network.class.getName()); + private static final Logger LOG = Logger.getLogger(v130Network.class.getName()); - public v127Network(DevTools devTools) { + public v130Network(DevTools devTools) { super(devTools); } @Override protected Command setUserAgentOverride(UserAgent userAgent) { - return org.openqa.selenium.devtools.v127.network.Network.setUserAgentOverride( + return org.openqa.selenium.devtools.v130.network.Network.setUserAgentOverride( userAgent.userAgent(), userAgent.acceptLanguage(), userAgent.platform(), Optional.empty()); } @Override protected Command enableNetworkCaching() { - return org.openqa.selenium.devtools.v127.network.Network.setCacheDisabled(false); + return org.openqa.selenium.devtools.v130.network.Network.setCacheDisabled(false); } @Override protected Command disableNetworkCaching() { - return org.openqa.selenium.devtools.v127.network.Network.setCacheDisabled(true); + return org.openqa.selenium.devtools.v130.network.Network.setCacheDisabled(true); } @Override diff --git a/java/src/org/openqa/selenium/devtools/v127/v127Target.java b/java/src/org/openqa/selenium/devtools/v130/v130Target.java similarity index 83% rename from java/src/org/openqa/selenium/devtools/v127/v127Target.java rename to java/src/org/openqa/selenium/devtools/v130/v130Target.java index 84831ac44ab6b..e64a8987d36be 100644 --- a/java/src/org/openqa/selenium/devtools/v127/v127Target.java +++ b/java/src/org/openqa/selenium/devtools/v130/v130Target.java @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -package org.openqa.selenium.devtools.v127; +package org.openqa.selenium.devtools.v130; import java.util.List; import java.util.Map; @@ -28,21 +28,21 @@ import org.openqa.selenium.devtools.idealized.browser.model.BrowserContextID; import org.openqa.selenium.devtools.idealized.target.model.SessionID; import org.openqa.selenium.devtools.idealized.target.model.TargetID; -import org.openqa.selenium.devtools.v127.target.Target; -import org.openqa.selenium.devtools.v127.target.model.TargetInfo; +import org.openqa.selenium.devtools.v130.target.Target; +import org.openqa.selenium.devtools.v130.target.model.TargetInfo; import org.openqa.selenium.json.JsonInput; import org.openqa.selenium.json.TypeToken; -public class v127Target implements org.openqa.selenium.devtools.idealized.target.Target { +public class v130Target implements org.openqa.selenium.devtools.idealized.target.Target { @Override public Command detachFromTarget( Optional sessionId, Optional targetId) { return Target.detachFromTarget( sessionId.map( - id -> new org.openqa.selenium.devtools.v127.target.model.SessionID(id.toString())), + id -> new org.openqa.selenium.devtools.v130.target.model.SessionID(id.toString())), targetId.map( - id -> new org.openqa.selenium.devtools.v127.target.model.TargetID(id.toString()))); + id -> new org.openqa.selenium.devtools.v130.target.model.TargetID(id.toString()))); } @Override @@ -74,19 +74,19 @@ public Command detachFromTarget( @Override public Command attachToTarget(TargetID targetId) { - Function mapper = + Function mapper = ConverterFunctions.map( - "sessionId", org.openqa.selenium.devtools.v127.target.model.SessionID.class); + "sessionId", org.openqa.selenium.devtools.v130.target.model.SessionID.class); return new Command<>( "Target.attachToTarget", Map.of( "targetId", - new org.openqa.selenium.devtools.v127.target.model.TargetID(targetId.toString()), + new org.openqa.selenium.devtools.v130.target.model.TargetID(targetId.toString()), "flatten", true), input -> { - org.openqa.selenium.devtools.v127.target.model.SessionID id = mapper.apply(input); + org.openqa.selenium.devtools.v130.target.model.SessionID id = mapper.apply(input); return new SessionID(id.toString()); }); } @@ -101,9 +101,9 @@ public Event detached() { return new Event<>( "Target.detachedFromTarget", input -> { - Function converter = + Function converter = ConverterFunctions.map( - "targetId", org.openqa.selenium.devtools.v127.target.model.TargetID.class); + "targetId", org.openqa.selenium.devtools.v130.target.model.TargetID.class); return new TargetID(converter.apply(input).toString()); }); } diff --git a/java/src/org/openqa/selenium/devtools/versions.bzl b/java/src/org/openqa/selenium/devtools/versions.bzl index 32e5565a8a838..59129be697b12 100644 --- a/java/src/org/openqa/selenium/devtools/versions.bzl +++ b/java/src/org/openqa/selenium/devtools/versions.bzl @@ -2,7 +2,7 @@ CDP_VERSIONS = [ "v85", # Required by Firefox "v128", "v129", - "v127", + "v130", ] CDP_DEPS = ["//java/src/org/openqa/selenium/devtools/%s" % v for v in CDP_VERSIONS] diff --git a/java/src/org/openqa/selenium/grid/node/Node.java b/java/src/org/openqa/selenium/grid/node/Node.java index 09fe7d02ae9f5..bc2b5c75b5ab7 100644 --- a/java/src/org/openqa/selenium/grid/node/Node.java +++ b/java/src/org/openqa/selenium/grid/node/Node.java @@ -101,6 +101,12 @@ * by {@code sessionId}. This returns a boolean. * * + * POST + * /se/grid/node/connection/{sessionId} + * Allows the node to be ask about whether or not new websocket connections are allowed for the {@link Session} + * identified by {@code sessionId}. This returns a boolean. + * + * * * * /session/{sessionId}/* * The request is forwarded to the {@link Session} identified by {@code sessionId}. When the @@ -172,6 +178,9 @@ protected Node( get("/se/grid/node/owner/{sessionId}") .to(params -> new IsSessionOwner(this, sessionIdFrom(params))) .with(spanDecorator("node.is_session_owner").andThen(requiresSecret)), + post("/se/grid/node/connection/{sessionId}") + .to(params -> new TryAcquireConnection(this, sessionIdFrom(params))) + .with(spanDecorator("node.is_session_owner").andThen(requiresSecret)), delete("/se/grid/node/session/{sessionId}") .to(params -> new StopNodeSession(this, sessionIdFrom(params))) .with(spanDecorator("node.stop_session").andThen(requiresSecret)), @@ -244,6 +253,8 @@ public TemporaryFilesystem getDownloadsFilesystem(UUID uuid) throws IOException public abstract boolean isSessionOwner(SessionId id); + public abstract boolean tryAcquireConnection(SessionId id); + public abstract boolean isSupporting(Capabilities capabilities); public abstract NodeStatus getStatus(); diff --git a/java/src/org/openqa/selenium/grid/node/ProxyNodeWebsockets.java b/java/src/org/openqa/selenium/grid/node/ProxyNodeWebsockets.java index e3f656c069125..eff13dc5a40f5 100644 --- a/java/src/org/openqa/selenium/grid/node/ProxyNodeWebsockets.java +++ b/java/src/org/openqa/selenium/grid/node/ProxyNodeWebsockets.java @@ -94,6 +94,13 @@ public Optional> apply(String uri, Consumer downstrea return Optional.empty(); } + // ensure one session does not open to many connections, this might have a negative impact on + // the grid health + if (!node.tryAcquireConnection(id)) { + LOG.warning("Too many websocket connections initiated by " + id); + return Optional.empty(); + } + Session session = node.getSession(id); Capabilities caps = session.getCapabilities(); LOG.fine("Scanning for endpoint: " + caps); diff --git a/java/src/org/openqa/selenium/grid/node/TryAcquireConnection.java b/java/src/org/openqa/selenium/grid/node/TryAcquireConnection.java new file mode 100644 index 0000000000000..6c8822bea84cd --- /dev/null +++ b/java/src/org/openqa/selenium/grid/node/TryAcquireConnection.java @@ -0,0 +1,45 @@ +// Licensed to the Software Freedom Conservancy (SFC) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The SFC licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +package org.openqa.selenium.grid.node; + +import static org.openqa.selenium.remote.http.Contents.asJson; + +import com.google.common.collect.ImmutableMap; +import java.io.UncheckedIOException; +import org.openqa.selenium.internal.Require; +import org.openqa.selenium.remote.SessionId; +import org.openqa.selenium.remote.http.HttpHandler; +import org.openqa.selenium.remote.http.HttpRequest; +import org.openqa.selenium.remote.http.HttpResponse; + +class TryAcquireConnection implements HttpHandler { + + private final Node node; + private final SessionId id; + + TryAcquireConnection(Node node, SessionId id) { + this.node = Require.nonNull("Node", node); + this.id = Require.nonNull("Session id", id); + } + + @Override + public HttpResponse execute(HttpRequest req) throws UncheckedIOException { + return new HttpResponse() + .setContent(asJson(ImmutableMap.of("value", node.tryAcquireConnection(id)))); + } +} diff --git a/java/src/org/openqa/selenium/grid/node/config/NodeFlags.java b/java/src/org/openqa/selenium/grid/node/config/NodeFlags.java index 800a0798a4e17..b56e57b3dcb97 100644 --- a/java/src/org/openqa/selenium/grid/node/config/NodeFlags.java +++ b/java/src/org/openqa/selenium/grid/node/config/NodeFlags.java @@ -18,6 +18,7 @@ package org.openqa.selenium.grid.node.config; import static org.openqa.selenium.grid.config.StandardGridRoles.NODE_ROLE; +import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_CONNECTION_LIMIT; import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_DETECT_DRIVERS; import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_DRAIN_AFTER_SESSION_COUNT; import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_ENABLE_BIDI; @@ -77,6 +78,14 @@ public class NodeFlags implements HasRoles { @ConfigValue(section = NODE_SECTION, name = "session-timeout", example = "60") public int sessionTimeout = DEFAULT_SESSION_TIMEOUT; + @Parameter( + names = {"--connection-limit-per-session"}, + description = + "Let X be the maximum number of websocket connections per session.This will ensure one" + + " session is not able to exhaust the connection limit of the host") + @ConfigValue(section = NODE_SECTION, name = "connection-limit-per-session", example = "8") + public int connectionLimitPerSession = DEFAULT_CONNECTION_LIMIT; + @Parameter( names = {"--detect-drivers"}, arity = 1, diff --git a/java/src/org/openqa/selenium/grid/node/config/NodeOptions.java b/java/src/org/openqa/selenium/grid/node/config/NodeOptions.java index 7317f6e7c8870..ff8fc6d76667a 100644 --- a/java/src/org/openqa/selenium/grid/node/config/NodeOptions.java +++ b/java/src/org/openqa/selenium/grid/node/config/NodeOptions.java @@ -73,6 +73,7 @@ public class NodeOptions { public static final int DEFAULT_HEARTBEAT_PERIOD = 60; public static final int DEFAULT_SESSION_TIMEOUT = 300; public static final int DEFAULT_DRAIN_AFTER_SESSION_COUNT = 0; + public static final int DEFAULT_CONNECTION_LIMIT = 10; public static final boolean DEFAULT_ENABLE_CDP = true; public static final boolean DEFAULT_ENABLE_BIDI = true; static final String NODE_SECTION = "node"; @@ -262,6 +263,15 @@ public int getMaxSessions() { return Math.min(maxSessions, DEFAULT_MAX_SESSIONS); } + public int getConnectionLimitPerSession() { + int connectionLimit = + config + .getInt(NODE_SECTION, "connection-limit-per-session") + .orElse(DEFAULT_CONNECTION_LIMIT); + Require.positive("Session connection limit", connectionLimit); + return connectionLimit; + } + public Duration getSessionTimeout() { // If the user sets 10s or less, we default to 10s. int seconds = diff --git a/java/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java b/java/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java index d293d1c6ba78c..af8c05cf7a7c1 100644 --- a/java/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java +++ b/java/src/org/openqa/selenium/grid/node/k8s/OneShotNode.java @@ -34,6 +34,7 @@ import java.util.Optional; import java.util.ServiceLoader; import java.util.UUID; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; import java.util.stream.StreamSupport; import org.openqa.selenium.Capabilities; @@ -98,6 +99,8 @@ public class OneShotNode extends Node { private final Duration heartbeatPeriod; private final URI gridUri; private final UUID slotId = UUID.randomUUID(); + private final int connectionLimitPerSession; + private final AtomicInteger connectionCounter = new AtomicInteger(); private RemoteWebDriver driver; private SessionId sessionId; private HttpClient client; @@ -114,7 +117,8 @@ private OneShotNode( URI uri, URI gridUri, Capabilities stereotype, - WebDriverInfo driverInfo) { + WebDriverInfo driverInfo, + int connectionLimitPerSession) { super(tracer, id, uri, registrationSecret, Require.positive(sessionTimeout)); this.heartbeatPeriod = heartbeatPeriod; @@ -122,6 +126,7 @@ private OneShotNode( this.gridUri = Require.nonNull("Public Grid URI", gridUri); this.stereotype = ImmutableCapabilities.copyOf(Require.nonNull("Stereotype", stereotype)); this.driverInfo = Require.nonNull("Driver info", driverInfo); + this.connectionLimitPerSession = connectionLimitPerSession; new JMXHelper().register(this); } @@ -177,7 +182,8 @@ public static Node create(Config config) { .getPublicGridUri() .orElseThrow(() -> new ConfigException("Unable to determine public grid address")), stereotype, - driverInfo); + driverInfo, + nodeOptions.getConnectionLimitPerSession()); } @Override @@ -357,6 +363,11 @@ public boolean isSessionOwner(SessionId id) { return driver != null && sessionId.equals(id); } + @Override + public boolean tryAcquireConnection(SessionId id) { + return sessionId.equals(id) && connectionLimitPerSession > connectionCounter.getAndIncrement(); + } + @Override public boolean isSupporting(Capabilities capabilities) { return driverInfo.isSupporting(capabilities); diff --git a/java/src/org/openqa/selenium/grid/node/local/LocalNode.java b/java/src/org/openqa/selenium/grid/node/local/LocalNode.java index 7304db8a87847..b42c557c91008 100644 --- a/java/src/org/openqa/selenium/grid/node/local/LocalNode.java +++ b/java/src/org/openqa/selenium/grid/node/local/LocalNode.java @@ -59,6 +59,7 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; @@ -127,6 +128,7 @@ public class LocalNode extends Node { private final int configuredSessionCount; private final boolean cdpEnabled; private final boolean managedDownloadsEnabled; + private final int connectionLimitPerSession; private final boolean bidiEnabled; private final AtomicBoolean drainAfterSessions = new AtomicBoolean(); @@ -153,7 +155,8 @@ protected LocalNode( Duration heartbeatPeriod, List factories, Secret registrationSecret, - boolean managedDownloadsEnabled) { + boolean managedDownloadsEnabled, + int connectionLimitPerSession) { super( tracer, new NodeId(UUID.randomUUID()), @@ -176,6 +179,7 @@ protected LocalNode( this.cdpEnabled = cdpEnabled; this.bidiEnabled = bidiEnabled; this.managedDownloadsEnabled = managedDownloadsEnabled; + this.connectionLimitPerSession = connectionLimitPerSession; this.healthCheck = healthCheck == null @@ -579,6 +583,24 @@ public boolean isSessionOwner(SessionId id) { return currentSessions.getIfPresent(id) != null; } + @Override + public boolean tryAcquireConnection(SessionId id) throws NoSuchSessionException { + SessionSlot slot = currentSessions.getIfPresent(id); + + if (slot == null) { + return false; + } + + if (connectionLimitPerSession == -1) { + // no limit + return true; + } + + AtomicLong counter = slot.getConnectionCounter(); + + return connectionLimitPerSession > counter.getAndIncrement(); + } + @Override public Session getSession(SessionId id) throws NoSuchSessionException { Require.nonNull("Session ID", id); @@ -987,6 +1009,7 @@ public static class Builder { private HealthCheck healthCheck; private Duration heartbeatPeriod = Duration.ofSeconds(NodeOptions.DEFAULT_HEARTBEAT_PERIOD); private boolean managedDownloadsEnabled = false; + private int connectionLimitPerSession = -1; private Builder(Tracer tracer, EventBus bus, URI uri, URI gridUri, Secret registrationSecret) { this.tracer = Require.nonNull("Tracer", tracer); @@ -1041,6 +1064,11 @@ public Builder enableManagedDownloads(boolean enable) { return this; } + public Builder connectionLimitPerSession(int connectionLimitPerSession) { + this.connectionLimitPerSession = connectionLimitPerSession; + return this; + } + public LocalNode build() { return new LocalNode( tracer, @@ -1057,7 +1085,8 @@ public LocalNode build() { heartbeatPeriod, factories.build(), registrationSecret, - managedDownloadsEnabled); + managedDownloadsEnabled, + connectionLimitPerSession); } public Advanced advanced() { diff --git a/java/src/org/openqa/selenium/grid/node/local/LocalNodeFactory.java b/java/src/org/openqa/selenium/grid/node/local/LocalNodeFactory.java index 4224b2483f9db..600f516b02992 100644 --- a/java/src/org/openqa/selenium/grid/node/local/LocalNodeFactory.java +++ b/java/src/org/openqa/selenium/grid/node/local/LocalNodeFactory.java @@ -70,7 +70,8 @@ public static Node create(Config config) { .enableCdp(nodeOptions.isCdpEnabled()) .enableBiDi(nodeOptions.isBiDiEnabled()) .enableManagedDownloads(nodeOptions.isManagedDownloadsEnabled()) - .heartbeatPeriod(nodeOptions.getHeartbeatPeriod()); + .heartbeatPeriod(nodeOptions.getHeartbeatPeriod()) + .connectionLimitPerSession(nodeOptions.getConnectionLimitPerSession()); List> builders = new ArrayList<>(); ServiceLoader.load(DriverService.Builder.class).forEach(builders::add); diff --git a/java/src/org/openqa/selenium/grid/node/local/SessionSlot.java b/java/src/org/openqa/selenium/grid/node/local/SessionSlot.java index 5b84accc84c31..3c51b785c13c0 100644 --- a/java/src/org/openqa/selenium/grid/node/local/SessionSlot.java +++ b/java/src/org/openqa/selenium/grid/node/local/SessionSlot.java @@ -21,6 +21,7 @@ import java.util.ServiceLoader; import java.util.UUID; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; import java.util.function.Predicate; import java.util.logging.Level; @@ -59,6 +60,7 @@ public class SessionSlot private final AtomicBoolean reserved = new AtomicBoolean(false); private final boolean supportingCdp; private final boolean supportingBiDi; + private final AtomicLong connectionCounter; private ActiveSession currentSession; public SessionSlot(EventBus bus, Capabilities stereotype, SessionFactory factory) { @@ -68,6 +70,7 @@ public SessionSlot(EventBus bus, Capabilities stereotype, SessionFactory factory this.factory = Require.nonNull("Session factory", factory); this.supportingCdp = isSlotSupportingCdp(this.stereotype); this.supportingBiDi = isSlotSupportingBiDi(this.stereotype); + this.connectionCounter = new AtomicLong(); } public UUID getId() { @@ -112,6 +115,7 @@ public void stop() { LOG.log(Level.WARNING, "Unable to cleanly close session", e); } currentSession = null; + connectionCounter.set(0); release(); bus.fire(new SessionClosedEvent(id)); LOG.info(String.format("Stopping session %s", id)); @@ -148,6 +152,7 @@ public Either apply(CreateSessionRequest sess if (possibleSession.isRight()) { ActiveSession session = possibleSession.right(); currentSession = session; + connectionCounter.set(0); return Either.right(session); } else { return Either.left(possibleSession.left()); @@ -185,4 +190,8 @@ public boolean hasRelayFactory() { public boolean isRelayServiceUp() { return hasRelayFactory() && ((RelaySessionFactory) factory).isServiceUp(); } + + public AtomicLong getConnectionCounter() { + return connectionCounter; + } } diff --git a/java/src/org/openqa/selenium/grid/node/remote/RemoteNode.java b/java/src/org/openqa/selenium/grid/node/remote/RemoteNode.java index ae7cc8e1af9fb..5df5da5969c42 100644 --- a/java/src/org/openqa/selenium/grid/node/remote/RemoteNode.java +++ b/java/src/org/openqa/selenium/grid/node/remote/RemoteNode.java @@ -174,6 +174,18 @@ public boolean isSessionOwner(SessionId id) { return Boolean.TRUE.equals(Values.get(res, Boolean.class)); } + @Override + public boolean tryAcquireConnection(SessionId id) { + Require.nonNull("Session ID", id); + + HttpRequest req = new HttpRequest(POST, "/se/grid/node/connection/" + id); + HttpTracing.inject(tracer, tracer.getCurrentContext(), req); + + HttpResponse res = client.with(addSecret).execute(req); + + return Boolean.TRUE.equals(Values.get(res, Boolean.class)); + } + @Override public Session getSession(SessionId id) throws NoSuchSessionException { Require.nonNull("Session ID", id); diff --git a/java/src/org/openqa/selenium/netty/server/SeleniumHandler.java b/java/src/org/openqa/selenium/netty/server/SeleniumHandler.java index a13ddc33ae68d..73dbd2a45aadf 100644 --- a/java/src/org/openqa/selenium/netty/server/SeleniumHandler.java +++ b/java/src/org/openqa/selenium/netty/server/SeleniumHandler.java @@ -19,8 +19,10 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import org.openqa.selenium.internal.Require; import org.openqa.selenium.remote.ErrorFilter; import org.openqa.selenium.remote.http.HttpHandler; @@ -31,18 +33,27 @@ class SeleniumHandler extends SimpleChannelInboundHandler { private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool(); private final HttpHandler seleniumHandler; + private Future lastOne; public SeleniumHandler(HttpHandler seleniumHandler) { super(HttpRequest.class); this.seleniumHandler = Require.nonNull("HTTP handler", seleniumHandler).with(new ErrorFilter()); + this.lastOne = CompletableFuture.completedFuture(null); } @Override protected void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) { - EXECUTOR.submit( - () -> { - HttpResponse res = seleniumHandler.execute(msg); - ctx.writeAndFlush(res); - }); + lastOne = + EXECUTOR.submit( + () -> { + HttpResponse res = seleniumHandler.execute(msg); + ctx.writeAndFlush(res); + }); + } + + @Override + public void channelInactive(ChannelHandlerContext ctx) throws Exception { + lastOne.cancel(true); + super.channelInactive(ctx); } } diff --git a/java/src/org/openqa/selenium/remote/http/HttpClient.java b/java/src/org/openqa/selenium/remote/http/HttpClient.java index ee7f8613a64ea..6cc86a6418a2f 100644 --- a/java/src/org/openqa/selenium/remote/http/HttpClient.java +++ b/java/src/org/openqa/selenium/remote/http/HttpClient.java @@ -23,6 +23,7 @@ import java.net.URL; import java.util.ServiceLoader; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import org.openqa.selenium.internal.Require; @@ -32,6 +33,10 @@ public interface HttpClient extends Closeable, HttpHandler { WebSocket openSocket(HttpRequest request, WebSocket.Listener listener); + default CompletableFuture executeAsync(HttpRequest req) { + return CompletableFuture.supplyAsync(() -> execute(req)); + } + default void close() {} interface Factory { diff --git a/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpClient.java b/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpClient.java index 51e4ede37d563..d0b9f76d41e57 100644 --- a/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpClient.java +++ b/java/src/org/openqa/selenium/remote/http/jdk/JdkHttpClient.java @@ -44,6 +44,7 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Supplier; @@ -369,9 +370,65 @@ private URI getWebSocketUri(HttpRequest request) throws URISyntaxException { return uri; } + @Override + public CompletableFuture executeAsync(HttpRequest request) { + // the facade for this http request + CompletableFuture cf = new CompletableFuture<>(); + + // the actual http request + Future future = + executorService.submit( + () -> { + try { + HttpResponse response = handler.execute(request); + + cf.complete(response); + } catch (Throwable t) { + cf.completeExceptionally(t); + } + }); + + cf.whenComplete( + (result, throwable) -> { + if (throwable instanceof java.util.concurrent.CancellationException) { + // try to interrupt the http request in case someone canceled the future returned + future.cancel(true); + } else if (throwable instanceof java.util.concurrent.TimeoutException) { + // try to interrupt the http request in case of a timeout, to avoid + // https://bugs.openjdk.org/browse/JDK-8258397 + future.cancel(true); + } + }); + + // will complete exceptionally with a java.util.concurrent.TimeoutException + return cf.orTimeout(readTimeout.toMillis(), TimeUnit.MILLISECONDS); + } + @Override public HttpResponse execute(HttpRequest req) throws UncheckedIOException { - return handler.execute(req); + Future async = executeAsync(req); + try { + // executeAsync does define a timeout, no need to use a timeout here + return async.get(); + } catch (CancellationException e) { + throw new WebDriverException(e.getMessage(), e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + async.cancel(true); + throw new WebDriverException(e.getMessage(), e); + } catch (ExecutionException e) { + Throwable cause = e.getCause(); + + if (cause instanceof java.util.concurrent.TimeoutException) { + throw new TimeoutException(cause); + } else if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } else if (cause instanceof Error) { + throw (Error) cause; + } + + throw new WebDriverException((cause != null) ? cause : e); + } } private HttpResponse execute0(HttpRequest req) throws UncheckedIOException { @@ -390,34 +447,13 @@ private HttpResponse execute0(HttpRequest req) throws UncheckedIOException { // - avoid a downgrade of POST requests, see the javadoc of j.n.h.HttpClient.Redirect // - not run into https://bugs.openjdk.org/browse/JDK-8304701 for (int i = 0; i < 100; i++) { - java.net.http.HttpRequest request = messages.createRequest(req, method, rawUri); - java.net.http.HttpResponse response; - - // use sendAsync to not run into https://bugs.openjdk.org/browse/JDK-8258397 - CompletableFuture> future = - client.sendAsync(request, byteHandler); - - try { - response = future.get(readTimeout.toMillis(), TimeUnit.MILLISECONDS); - } catch (CancellationException e) { - throw new WebDriverException(e.getMessage(), e); - } catch (ExecutionException e) { - Throwable cause = e.getCause(); - - if (cause instanceof HttpTimeoutException) { - throw new TimeoutException(cause); - } else if (cause instanceof IOException) { - throw (IOException) cause; - } else if (cause instanceof RuntimeException) { - throw (RuntimeException) cause; - } - - throw new WebDriverException((cause != null) ? cause : e); - } catch (java.util.concurrent.TimeoutException e) { - future.cancel(true); - throw new TimeoutException(e); + if (Thread.interrupted()) { + throw new InterruptedException("http request has been interrupted"); } + java.net.http.HttpRequest request = messages.createRequest(req, method, rawUri); + java.net.http.HttpResponse response = client.send(request, byteHandler); + switch (response.statusCode()) { case 303: method = HttpMethod.GET; @@ -454,11 +490,13 @@ private HttpResponse execute0(HttpRequest req) throws UncheckedIOException { } throw new ProtocolException("Too many redirects: 101"); + } catch (HttpTimeoutException e) { + throw new TimeoutException(e); } catch (IOException e) { throw new UncheckedIOException(e); } catch (InterruptedException e) { Thread.currentThread().interrupt(); - throw new RuntimeException(e); + throw new WebDriverException(e.getMessage(), e); } finally { LOG.log( Level.FINE, diff --git a/java/test/org/openqa/selenium/bidi/browsingcontext/LocateNodesTest.java b/java/test/org/openqa/selenium/bidi/browsingcontext/LocateNodesTest.java index 9b153e92954c8..c1f967ebc36ba 100644 --- a/java/test/org/openqa/selenium/bidi/browsingcontext/LocateNodesTest.java +++ b/java/test/org/openqa/selenium/bidi/browsingcontext/LocateNodesTest.java @@ -162,8 +162,6 @@ void canLocateNodesWithMaxNodeCount() { } @Test - @NotYetImplemented(CHROME) - @NotYetImplemented(EDGE) void canLocateNodesGivenStartNodes() { String handle = driver.getWindowHandle(); BrowsingContext browsingContext = new BrowsingContext(driver, handle); diff --git a/java/test/org/openqa/selenium/grid/distributor/AddingNodesTest.java b/java/test/org/openqa/selenium/grid/distributor/AddingNodesTest.java index 0649e1bbee235..1485d04fca4c6 100644 --- a/java/test/org/openqa/selenium/grid/distributor/AddingNodesTest.java +++ b/java/test/org/openqa/selenium/grid/distributor/AddingNodesTest.java @@ -445,6 +445,11 @@ public boolean isSessionOwner(SessionId id) { return running != null && running.getId().equals(id); } + @Override + public boolean tryAcquireConnection(SessionId id) { + return false; + } + @Override public boolean isSupporting(Capabilities capabilities) { return Objects.equals("cake", capabilities.getCapability("cheese")); diff --git a/java/test/org/openqa/selenium/netty/server/NettyServerTest.java b/java/test/org/openqa/selenium/netty/server/NettyServerTest.java index 04ba014a5be17..fd110c233e80e 100644 --- a/java/test/org/openqa/selenium/netty/server/NettyServerTest.java +++ b/java/test/org/openqa/selenium/netty/server/NettyServerTest.java @@ -28,14 +28,20 @@ import com.google.common.collect.ImmutableMap; import java.net.URL; +import java.time.Duration; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.openqa.selenium.TimeoutException; import org.openqa.selenium.grid.config.CompoundConfig; import org.openqa.selenium.grid.config.Config; import org.openqa.selenium.grid.config.MapConfig; import org.openqa.selenium.grid.server.BaseServerOptions; import org.openqa.selenium.grid.server.Server; import org.openqa.selenium.net.PortProber; +import org.openqa.selenium.remote.http.ClientConfig; import org.openqa.selenium.remote.http.HttpClient; import org.openqa.selenium.remote.http.HttpRequest; import org.openqa.selenium.remote.http.HttpResponse; @@ -141,6 +147,43 @@ void shouldNotBindToHost() { assertEquals("anyRandomHost", server.getUrl().getHost()); } + @Test + void doesInterruptPending() throws Exception { + CountDownLatch interrupted = new CountDownLatch(1); + Config cfg = new MapConfig(ImmutableMap.of()); + BaseServerOptions options = new BaseServerOptions(cfg); + + Server server = + new NettyServer( + options, + req -> { + try { + Thread.sleep(800); + } catch (InterruptedException ex) { + interrupted.countDown(); + } + return new HttpResponse(); + }) + .start(); + ClientConfig config = + ClientConfig.defaultConfig() + .readTimeout(Duration.ofMillis(400)) + .baseUri(server.getUrl().toURI()); + + // provoke a client timeout + Assertions.assertThrows( + TimeoutException.class, + () -> { + try (HttpClient client = HttpClient.Factory.createDefault().createClient(config)) { + HttpRequest request = new HttpRequest(DELETE, "/session"); + request.setHeader("Accept", "*/*"); + client.execute(request); + } + }); + + assertTrue(interrupted.await(1000, TimeUnit.MILLISECONDS), "The handling was interrupted"); + } + private void outputHeaders(HttpResponse res) { res.forEachHeader((name, value) -> System.out.printf("%s -> %s\n", name, value)); } diff --git a/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java b/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java index a50b4c11f75e3..55f1b71de47a8 100644 --- a/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java +++ b/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java @@ -35,6 +35,9 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; import java.util.logging.Logger; import java.util.stream.StreamSupport; import org.junit.jupiter.api.AfterAll; @@ -43,6 +46,7 @@ import org.openqa.selenium.BuildInfo; import org.openqa.selenium.Platform; import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.WebDriverException; import org.openqa.selenium.environment.webserver.AppServer; import org.openqa.selenium.environment.webserver.NettyAppServer; import org.openqa.selenium.json.Json; @@ -57,7 +61,7 @@ public abstract class HttpClientTestBase { protected abstract HttpClient.Factory createFactory(); - static HttpHandler delegate; + static volatile HttpHandler delegate; static AppServer server; private static final Logger LOG = Logger.getLogger(HttpClientTestBase.class.getName()); @@ -233,6 +237,92 @@ public void shouldAllowConfigurationFromSystemProperties() { } } + private ClientConfig prepareShouldStopTest( + CountDownLatch executing, CountDownLatch interrupted, int timeout) { + CountDownLatch unlock = new CountDownLatch(1); + + delegate = + req -> { + try { + unlock.await(20, TimeUnit.SECONDS); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + throw new RuntimeException(ex); + } + + return new HttpResponse(); + }; + + return ClientConfig.defaultConfig() + .withFilter( + (handler) -> + (request) -> { + try { + executing.countDown(); + return handler.execute(request); + } catch (WebDriverException ex) { + if (ex.getCause() instanceof InterruptedException) { + interrupted.countDown(); + } + + throw ex; + } finally { + unlock.countDown(); + } + }) + .readTimeout(Duration.ofMillis(timeout)); + } + + @Test + public void shouldStopRequestAfterTimeout() throws InterruptedException { + CountDownLatch executing = new CountDownLatch(1); + CountDownLatch interrupted = new CountDownLatch(1); + ClientConfig clientConfig = prepareShouldStopTest(executing, interrupted, 400); + + try (HttpClient client = + createFactory().createClient(clientConfig.baseUri(URI.create(server.whereIs("/"))))) { + HttpRequest request = new HttpRequest(GET, "/delayed"); + + assertThatExceptionOfType(TimeoutException.class).isThrownBy(() -> client.execute(request)); + assertThat(interrupted.await(800, TimeUnit.MILLISECONDS)).isTrue(); + } + } + + @Test + public void shouldStopAsyncRequestAfterTimeout() throws InterruptedException { + CountDownLatch executing = new CountDownLatch(1); + CountDownLatch interrupted = new CountDownLatch(1); + ClientConfig clientConfig = prepareShouldStopTest(executing, interrupted, 400); + + try (HttpClient client = + createFactory().createClient(clientConfig.baseUri(URI.create(server.whereIs("/"))))) { + HttpRequest request = new HttpRequest(GET, "/delayed"); + // does intentionally not read the future + client.executeAsync(request); + assertThat(interrupted.await(800, TimeUnit.MILLISECONDS)).isTrue(); + } + } + + @Test + public void shouldStopRequestOnCancel() throws InterruptedException { + CountDownLatch executing = new CountDownLatch(1); + CountDownLatch interrupted = new CountDownLatch(1); + CountDownLatch unlock = new CountDownLatch(1); + ClientConfig clientConfig = prepareShouldStopTest(executing, interrupted, 4000); + + try (HttpClient client = + createFactory().createClient(clientConfig.baseUri(URI.create(server.whereIs("/"))))) { + HttpRequest request = new HttpRequest(GET, "/delayed"); + + Future future = client.executeAsync(request); + + assertThat(executing.await(800, TimeUnit.MILLISECONDS)).isTrue(); + assertThat(future.cancel(true)).isTrue(); + assertThat(interrupted.await(800, TimeUnit.MILLISECONDS)).isTrue(); + unlock.countDown(); + } + } + private HttpResponse getResponseWithHeaders(final Multimap headers) { return executeWithinServer( new HttpRequest(GET, "/foo"), diff --git a/java/version.bzl b/java/version.bzl index 20171265cb335..f882c57e1cd55 100644 --- a/java/version.bzl +++ b/java/version.bzl @@ -1,2 +1,2 @@ -SE_VERSION = "4.26.0-SNAPSHOT" +SE_VERSION = "4.27.0-SNAPSHOT" TOOLS_JAVA_VERSION = "17" diff --git a/javascript/node/selenium-webdriver/BUILD.bazel b/javascript/node/selenium-webdriver/BUILD.bazel index 1cb3a28ab17ab..874c0b18b8bb1 100644 --- a/javascript/node/selenium-webdriver/BUILD.bazel +++ b/javascript/node/selenium-webdriver/BUILD.bazel @@ -11,13 +11,13 @@ load("//javascript/private:browsers.bzl", "BROWSERS") npm_link_all_packages(name = "node_modules") -VERSION = "4.26.0-nightly202409202352" +VERSION = "4.27.0-nightly202410301443" BROWSER_VERSIONS = [ "v85", "v128", "v129", - "v127", + "v130", ] js_library( diff --git a/javascript/node/selenium-webdriver/CHANGES.md b/javascript/node/selenium-webdriver/CHANGES.md index 765a6a53a8cb3..451efc75eb701 100644 --- a/javascript/node/selenium-webdriver/CHANGES.md +++ b/javascript/node/selenium-webdriver/CHANGES.md @@ -1,3 +1,10 @@ +## 4.26.0 + +- Add CDP for Chrome 130 and remove 127 +- Fix sendKeys command fail on FileDetector.handleFile error (#14663) +- Update dependencies to latest versions to resolve security alerts +- Close BiDi websocket connection (#14507) + ## 4.25.0 - Add CDP for Chrome 129 and remove 126 diff --git a/javascript/node/selenium-webdriver/lib/webdriver.js b/javascript/node/selenium-webdriver/lib/webdriver.js index cb60563695a06..e004e4505c1e4 100644 --- a/javascript/node/selenium-webdriver/lib/webdriver.js +++ b/javascript/node/selenium-webdriver/lib/webdriver.js @@ -2752,6 +2752,7 @@ class WebElement { keys = await this.driver_.fileDetector_.handleFile(this.driver_, keys.join('')) } catch (ex) { this.log_.severe('Error trying parse string as a file with file detector; sending keys instead' + ex) + keys = keys.join('') } return this.execute_( diff --git a/javascript/node/selenium-webdriver/package.json b/javascript/node/selenium-webdriver/package.json index 31f7b92c3eeed..cb31db734d8ac 100644 --- a/javascript/node/selenium-webdriver/package.json +++ b/javascript/node/selenium-webdriver/package.json @@ -1,6 +1,6 @@ { "name": "selenium-webdriver", - "version": "4.26.0-nightly202409202352", + "version": "4.27.0-nightly202410301443", "description": "The official WebDriver JavaScript bindings from the Selenium project", "license": "Apache-2.0", "keywords": [ diff --git a/javascript/node/selenium-webdriver/test/lib/webdriver_test.js b/javascript/node/selenium-webdriver/test/lib/webdriver_test.js index f850c3e4fcac8..419fdf6f80212 100644 --- a/javascript/node/selenium-webdriver/test/lib/webdriver_test.js +++ b/javascript/node/selenium-webdriver/test/lib/webdriver_test.js @@ -932,6 +932,32 @@ describe('WebDriver', function () { return driver.findElement(By.id('foo')).sendKeys('original/', 'path') }) + + it('sendKeysWithAFileDetector_handlerError', function () { + let executor = new FakeExecutor() + .expect(CName.FIND_ELEMENT, { + using: 'css selector', + value: '*[id="foo"]', + }) + .andReturnSuccess(WebElement.buildId('one')) + .expect(CName.SEND_KEYS_TO_ELEMENT, { + id: WebElement.buildId('one'), + text: 'original/path', + value: 'original/path'.split(''), + }) + .andReturnSuccess() + .end() + + let driver = executor.createDriver() + let handleFile = function (d, path) { + assert.strictEqual(driver, d) + assert.strictEqual(path, 'original/path') + return Promise.reject('unhandled file error') + } + driver.setFileDetector({ handleFile }) + + return driver.findElement(By.id('foo')).sendKeys('original/', 'path') + }) }) describe('switchTo()', function () { diff --git a/py/BUILD.bazel b/py/BUILD.bazel index e2ba6dd2019f1..ac44993213066 100644 --- a/py/BUILD.bazel +++ b/py/BUILD.bazel @@ -62,13 +62,13 @@ compile_pip_requirements( ], ) -SE_VERSION = "4.26.0.dev202409202351" +SE_VERSION = "4.27.0.dev202410311942" BROWSER_VERSIONS = [ "v85", "v128", "v129", - "v127", + "v130", ] TEST_DEPS = [ diff --git a/py/CHANGES b/py/CHANGES index a4e6fd42cb226..b7dabbf9d6b9b 100644 --- a/py/CHANGES +++ b/py/CHANGES @@ -1,3 +1,23 @@ +Selenium 4.26.1 +* DeprecationWarning raised in default webdriver init (#14690) +* Remote connection use timeout from ClientConfig (#14692) +* Add backward compatibility for AppiumConnection (#14696) + +Selenium 4.26.0 +* Add CDP for Chrome 130 and remove 127 +* Added more internal logging for CDP (#14668) +* Set consistent polling across java and python for `WebDriverWait` methods (#14626) +* webkitgtk: log_path -> log_output (#14618) +* Implement configurable configuration class for the http client (#13286) +* Better compatibility with Appium-python (#14587) +* Avoid waiting indefinitely on a frozen chromedriver process (#14578) +* Allow logging diagnose in safari driver (#14606) +* Remote connection throws response status code when data is empty (#14601) +* Remove deprecated parameter from EdgeService (#14563) +* Allow driver path to be set using ENV variables (#14528) +* Remove un-needed print (#14562) +* Fix a bug in `bidi/session.py` by removing mutable object as default value for function argument (#14286) + Selenium 4.25.0 * Add CDP for Chrome 129 and remove 126 * fix type errors for `service.py`, `cdp.py`, `webelement.py` and `remote_connection.py` (#14448) diff --git a/py/docs/source/conf.py b/py/docs/source/conf.py index d0f2f6b947180..1d5148a0508fa 100644 --- a/py/docs/source/conf.py +++ b/py/docs/source/conf.py @@ -56,9 +56,9 @@ # built documents. # # The short X.Y version. -version = '4.26' +version = '4.27' # The full version, including alpha/beta/rc tags. -release = '4.26.0.dev202409202351' +release = '4.27.0.dev202410311942' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/py/mypy.ini b/py/mypy.ini deleted file mode 100644 index 9ccefe45dd10c..0000000000000 --- a/py/mypy.ini +++ /dev/null @@ -1,39 +0,0 @@ -; The aim in future here is we would be able to turn (most) of these flags on, however the typing technical -; debt is quite colossal right now. For now we should maybe get everything working with the config here -; then look at going after partially or completely untyped defs as a phase-2. -[mypy] -files = selenium -; warn about per-module sections in the config file that do not match any files processed. -warn_unused_configs = True -; disallows subclassing of typing.Any. -disallow_subclassing_any = False -; disallow usage of generic types that do not specify explicit type parameters. -disallow_any_generics = False -; disallow calling functions without type annotations from functions that have type annotations. -disallow_untyped_calls = False -; disallow defining functions without type annotations or with incomplete annotations. -disallow_untyped_defs = False -; disallow defining functions with incomplete type annotations. -disallow_incomplete_defs = False -; type-checks the interior of functions without type annotations. -check_untyped_defs = False -; reports an error whenever a function with type annotations is decorated with a decorator without annotations. -disallow_untyped_decorators = False -; changes the treatment of arguments with a default value of None by not implicitly making their type `typing.Optional`. -no_implicit_optional = False -; warns about casting an expression to it's inferred type. -warn_redundant_casts = True -; warns about unneeded `# type: ignore` comments. -warn_unused_ignores = True -; warns when returning a value with typing.Any from a function with a non typing.Any return type. -warn_return_any = False -; Shows a warning when encountering any code inferred to be unreachable after performing type analysis. -warn_unreachable = False - -[mypy-trio_websocket] -; suppress error messages about imports that cannot be resolved. -ignore_missing_imports = True - -[mypy-_winreg] -; suppress error messages about imports that cannot be resolved. -ignore_missing_imports = True diff --git a/py/pyproject.toml b/py/pyproject.toml index c17be72be8eb3..8a5e26071de6a 100644 --- a/py/pyproject.toml +++ b/py/pyproject.toml @@ -19,3 +19,61 @@ markers = [ ] python_files = ["test_*.py", "*_test.py"] testpaths = ["test"] + +# mypy global options +[tool.mypy] +# The aim in future here is we would be able to turn (most) of these flags on, however the typing technical +# debt is quite colossal right now. For now we should maybe get everything working with the config here +# then look at going after partially or completely untyped defs as a phase-2. +files = "selenium" +# warn about per-module sections in the config file that do not match any files processed. +warn_unused_configs = true +# disallows subclassing of typing.Any. +disallow_subclassing_any = false +# disallow usage of generic types that do not specify explicit type parameters. +disallow_any_generics = false +# disallow calling functions without type annotations from functions that have type annotations. +disallow_untyped_calls = false +# disallow defining functions without type annotations or with incomplete annotations. +disallow_untyped_defs = false +# disallow defining functions with incomplete type annotations. +disallow_incomplete_defs = false +# type-checks the interior of functions without type annotations. +check_untyped_defs = false +# reports an error whenever a function with type annotations is decorated with a decorator without annotations. +disallow_untyped_decorators = false +# changes the treatment of arguments with a default value of None by not implicitly making their type `typing.Optional`. +no_implicit_optional = false +# warns about casting an expression to it's inferred type. +warn_redundant_casts = true +# warns about unneeded `# type: ignore` comments. +warn_unused_ignores = true +# warns when returning a value with typing.Any from a function with a non typing.Any return type. +warn_return_any = false +# Shows a warning when encountering any code inferred to be unreachable after performing type analysis. +warn_unreachable = false + +# mypy module specific options +[[tool.mypy.trio_websocket]] +# suppress error messages about imports that cannot be resolved. +ignore_missing_imports = true + +[[tool.mypy._winreg]] +# suppress error messages about imports that cannot be resolved. +ignore_missing_imports = true + +[tool.isort] +# isort is a common python tool for keeping imports nicely formatted. +# Automatically keep imports alphabetically sorted, on single lines in +# PEP recommended sections (https://peps.python.org/pep-0008/#imports) +# files or individual lines can be ignored via `# isort:skip|# isort:skip_file`. +profile = "black" +py_version=38 +force_single_line = true + +[tool.black] +line-length = 120 +target-version = ['py38'] + +[tool.docformatter] +recursive = true diff --git a/py/selenium/__init__.py b/py/selenium/__init__.py index e4c5adeea398b..e7ab5cc0b075e 100644 --- a/py/selenium/__init__.py +++ b/py/selenium/__init__.py @@ -16,4 +16,4 @@ # under the License. -__version__ = "4.26.0.dev202409202351" +__version__ = "4.27.0.dev202410311942" diff --git a/py/selenium/webdriver/__init__.py b/py/selenium/webdriver/__init__.py index e61f321c187e0..b64471644e380 100644 --- a/py/selenium/webdriver/__init__.py +++ b/py/selenium/webdriver/__init__.py @@ -44,7 +44,7 @@ from .wpewebkit.service import Service as WPEWebKitService # noqa from .wpewebkit.webdriver import WebDriver as WPEWebKit # noqa -__version__ = "4.26.0.dev202409202351" +__version__ = "4.27.0.dev202410311942" # We need an explicit __all__ because the above won't otherwise be exported. __all__ = [ diff --git a/py/selenium/webdriver/chromium/remote_connection.py b/py/selenium/webdriver/chromium/remote_connection.py index 021c47737cd17..ea532cba8ddcd 100644 --- a/py/selenium/webdriver/chromium/remote_connection.py +++ b/py/selenium/webdriver/chromium/remote_connection.py @@ -30,9 +30,10 @@ def __init__( ignore_proxy: Optional[bool] = False, client_config: Optional[ClientConfig] = None, ) -> None: + client_config = client_config or ClientConfig( + remote_server_addr=remote_server_addr, keep_alive=keep_alive, timeout=120 + ) super().__init__( - remote_server_addr=remote_server_addr, - keep_alive=keep_alive, ignore_proxy=ignore_proxy, client_config=client_config, ) diff --git a/py/selenium/webdriver/common/bidi/cdp.py b/py/selenium/webdriver/common/bidi/cdp.py index c4cb0feeedf40..c9ed47825e4da 100644 --- a/py/selenium/webdriver/common/bidi/cdp.py +++ b/py/selenium/webdriver/common/bidi/cdp.py @@ -211,13 +211,19 @@ async def execute(self, cmd: typing.Generator[dict, T, typing.Any]) -> T: if self.session_id: request["sessionId"] = self.session_id request_str = json.dumps(request) + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f"Sending CDP message: {cmd_id} {cmd_event}: {request_str}") try: await self.ws.send_message(request_str) except WsConnectionClosed as wcc: raise CdpConnectionClosed(wcc.reason) from None await cmd_event.wait() response = self.inflight_result.pop(cmd_id) + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f"Received CDP message: {response}") if isinstance(response, Exception): + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f"Exception raised by {cmd_event} message: {type(response).__name__}") raise response return response diff --git a/py/selenium/webdriver/firefox/remote_connection.py b/py/selenium/webdriver/firefox/remote_connection.py index 502c144c41622..a749cce37dc62 100644 --- a/py/selenium/webdriver/firefox/remote_connection.py +++ b/py/selenium/webdriver/firefox/remote_connection.py @@ -32,9 +32,10 @@ def __init__( ignore_proxy: Optional[bool] = False, client_config: Optional[ClientConfig] = None, ) -> None: + client_config = client_config or ClientConfig( + remote_server_addr=remote_server_addr, keep_alive=keep_alive, timeout=120 + ) super().__init__( - remote_server_addr=remote_server_addr, - keep_alive=keep_alive, ignore_proxy=ignore_proxy, client_config=client_config, ) diff --git a/py/selenium/webdriver/ie/webdriver.py b/py/selenium/webdriver/ie/webdriver.py index 11c137e509fe7..4df2c989a5813 100644 --- a/py/selenium/webdriver/ie/webdriver.py +++ b/py/selenium/webdriver/ie/webdriver.py @@ -16,6 +16,7 @@ # under the License. from selenium.webdriver.common.driver_finder import DriverFinder +from selenium.webdriver.remote.client_config import ClientConfig from selenium.webdriver.remote.remote_connection import RemoteConnection from selenium.webdriver.remote.webdriver import WebDriver as RemoteWebDriver @@ -49,10 +50,10 @@ def __init__( self.service.path = self.service.env_path() or DriverFinder(self.service, options).get_driver_path() self.service.start() + client_config = ClientConfig(remote_server_addr=self.service.service_url, keep_alive=keep_alive, timeout=120) executor = RemoteConnection( - remote_server_addr=self.service.service_url, - keep_alive=keep_alive, ignore_proxy=options._ignore_local_proxy, + client_config=client_config, ) try: diff --git a/py/selenium/webdriver/remote/remote_connection.py b/py/selenium/webdriver/remote/remote_connection.py index 57f38b5806574..04786c39c2673 100644 --- a/py/selenium/webdriver/remote/remote_connection.py +++ b/py/selenium/webdriver/remote/remote_connection.py @@ -136,6 +136,18 @@ class RemoteConnection: """ browser_name = None + # Keep backward compatibility for AppiumConnection - https://github.com/SeleniumHQ/selenium/issues/14694 + import os + import socket + + import certifi + + _timeout = ( + float(os.getenv("GLOBAL_DEFAULT_TIMEOUT", str(socket.getdefaulttimeout()))) + if os.getenv("GLOBAL_DEFAULT_TIMEOUT") is not None + else socket.getdefaulttimeout() + ) + _ca_certs = os.getenv("REQUESTS_CA_BUNDLE") if "REQUESTS_CA_BUNDLE" in os.environ else certifi.where() _client_config: ClientConfig = None system = platform.system().lower() @@ -296,6 +308,9 @@ def __init__( init_args_for_pool_manager=init_args_for_pool_manager, ) + # Keep backward compatibility for AppiumConnection - https://github.com/SeleniumHQ/selenium/issues/14694 + RemoteConnection._timeout = self._client_config.timeout + RemoteConnection._ca_certs = self._client_config.ca_certs RemoteConnection._client_config = self._client_config if remote_server_addr: @@ -375,7 +390,7 @@ def execute(self, command, params): LOGGER.debug("%s %s %s", command_info[0], url, str(trimmed)) return self._request(command_info[0], url, body=data) - def _request(self, method, url, body=None, timeout=120): + def _request(self, method, url, body=None): """Send an HTTP request to the remote server. :Args: @@ -397,12 +412,12 @@ def _request(self, method, url, body=None, timeout=120): body = None if self._client_config.keep_alive: - response = self._conn.request(method, url, body=body, headers=headers, timeout=timeout) + response = self._conn.request(method, url, body=body, headers=headers, timeout=self._client_config.timeout) statuscode = response.status else: conn = self._get_connection_manager() with conn as http: - response = http.request(method, url, body=body, headers=headers, timeout=timeout) + response = http.request(method, url, body=body, headers=headers, timeout=self._client_config.timeout) statuscode = response.status data = response.data.decode("UTF-8") LOGGER.debug("Remote response: status=%s | data=%s | headers=%s", response.status, data, response.headers) diff --git a/py/selenium/webdriver/safari/remote_connection.py b/py/selenium/webdriver/safari/remote_connection.py index 05dbfb379b4c2..cd8762da4ce92 100644 --- a/py/selenium/webdriver/safari/remote_connection.py +++ b/py/selenium/webdriver/safari/remote_connection.py @@ -32,9 +32,10 @@ def __init__( ignore_proxy: Optional[bool] = False, client_config: Optional[ClientConfig] = None, ) -> None: + client_config = client_config or ClientConfig( + remote_server_addr=remote_server_addr, keep_alive=keep_alive, timeout=120 + ) super().__init__( - remote_server_addr=remote_server_addr, - keep_alive=keep_alive, ignore_proxy=ignore_proxy, client_config=client_config, ) diff --git a/py/selenium/webdriver/support/wait.py b/py/selenium/webdriver/support/wait.py index 949b47238d20e..35bd74a695eb8 100644 --- a/py/selenium/webdriver/support/wait.py +++ b/py/selenium/webdriver/support/wait.py @@ -99,9 +99,9 @@ def until(self, method: Callable[[D], Union[Literal[False], T]], message: str = except self._ignored_exceptions as exc: screen = getattr(exc, "screen", None) stacktrace = getattr(exc, "stacktrace", None) - time.sleep(self._poll) if time.monotonic() > end_time: break + time.sleep(self._poll) raise TimeoutException(message, screen, stacktrace) def until_not(self, method: Callable[[D], T], message: str = "") -> Union[T, Literal[True]]: @@ -122,7 +122,7 @@ def until_not(self, method: Callable[[D], T], message: str = "") -> Union[T, Lit return value except self._ignored_exceptions: return True - time.sleep(self._poll) if time.monotonic() > end_time: break + time.sleep(self._poll) raise TimeoutException(message) diff --git a/py/selenium/webdriver/webkitgtk/service.py b/py/selenium/webdriver/webkitgtk/service.py index 92cea26c535f3..5f42d31486143 100644 --- a/py/selenium/webdriver/webkitgtk/service.py +++ b/py/selenium/webdriver/webkitgtk/service.py @@ -15,6 +15,7 @@ # specific language governing permissions and limitations # under the License. import typing +import warnings from selenium.webdriver.common import service @@ -28,7 +29,7 @@ class Service(service.Service): :param executable_path: install path of the WebKitWebDriver executable, defaults to `WebKitWebDriver`. :param port: Port for the service to run on, defaults to 0 where the operating system will decide. :param service_args: (Optional) List of args to be passed to the subprocess when launching the executable. - :param log_path: (Optional) File path for the file to be opened and passed as the subprocess stdout/stderr handler. + :param log_output: (Optional) File path for the file to be opened and passed as the subprocess stdout/stderr handler. :param env: (Optional) Mapping of environment variables for the new process, defaults to `os.environ`. """ @@ -37,16 +38,20 @@ def __init__( executable_path: str = DEFAULT_EXECUTABLE_PATH, port: int = 0, log_path: typing.Optional[str] = None, + log_output: typing.Optional[str] = None, service_args: typing.Optional[typing.List[str]] = None, env: typing.Optional[typing.Mapping[str, str]] = None, **kwargs, ) -> None: self.service_args = service_args or [] - log_file = open(log_path, "wb") if log_path else None + if log_path is not None: + warnings.warn("log_path is deprecated, use log_output instead", DeprecationWarning, stacklevel=2) + log_path = open(log_path, "wb") + log_output = open(log_output, "wb") if log_output else None super().__init__( executable_path=executable_path, port=port, - log_file=log_file, + log_output=log_path or log_output, env=env, **kwargs, ) diff --git a/py/setup.cfg b/py/setup.cfg index 0cda7cace9e8c..c8eb38080b7d3 100644 --- a/py/setup.cfg +++ b/py/setup.cfg @@ -4,8 +4,3 @@ exclude = .tox,docs/source/conf.py,*venv extend-ignore = E501, E203 # This does nothing for now as E501 is ignored. max-line-length = 120 - -[tool:pytest] -addopts = -ra -python_files = test_*.py *_tests.py -testpaths = test diff --git a/py/setup.py b/py/setup.py index 9cdd0712f7158..cd6b84b8e2c14 100755 --- a/py/setup.py +++ b/py/setup.py @@ -28,7 +28,7 @@ setup_args = { 'cmdclass': {'install': install}, 'name': 'selenium', - 'version': "4.26.0.dev202409202351", + 'version': "4.27.0.dev202410311942", 'license': 'Apache 2.0', 'description': 'Official Python bindings for Selenium WebDriver.', 'long_description': open(join(abspath(dirname(__file__)), "README.rst")).read(), diff --git a/py/test/selenium/webdriver/safari/launcher_tests.py b/py/test/selenium/webdriver/safari/launcher_tests.py index 1d08a226d2fcd..77a57c313aa2e 100644 --- a/py/test/selenium/webdriver/safari/launcher_tests.py +++ b/py/test/selenium/webdriver/safari/launcher_tests.py @@ -52,6 +52,7 @@ def test_launch(self, driver): assert driver.capabilities["browserName"] == "safari" +@pytest.mark.skip(reason="Need to be updated") def test_launch_safari_with_legacy_flag(mocker, driver_class): import subprocess diff --git a/py/test/unit/selenium/webdriver/remote/remote_connection_tests.py b/py/test/unit/selenium/webdriver/remote/remote_connection_tests.py index e3d8e29f38e69..ea6281607b4be 100644 --- a/py/test/unit/selenium/webdriver/remote/remote_connection_tests.py +++ b/py/test/unit/selenium/webdriver/remote/remote_connection_tests.py @@ -307,6 +307,19 @@ def test_register_extra_headers(mock_request, remote_connection): assert headers["Foo"] == "bar" +def test_backwards_compatibility_with_appium_connection(): + # Keep backward compatibility for AppiumConnection - https://github.com/SeleniumHQ/selenium/issues/14694 + client_config = ClientConfig(remote_server_addr="http://remote", ca_certs="/path/to/cacert.pem", timeout=300) + remote_connection = RemoteConnection(client_config=client_config) + assert remote_connection._ca_certs == "/path/to/cacert.pem" + assert remote_connection._timeout == 300 + assert remote_connection._client_config == client_config + remote_connection.set_timeout(120) + assert remote_connection.get_timeout() == 120 + remote_connection.set_certificate_bundle_path("/path/to/cacert2.pem") + assert remote_connection.get_certificate_bundle_path() == "/path/to/cacert2.pem" + + def test_get_connection_manager_with_timeout_from_client_config(): remote_connection = RemoteConnection(remote_server_addr="http://remote", keep_alive=False) remote_connection.set_timeout(10) diff --git a/py/tox.ini b/py/tox.ini index f454af1ee3347..fefb4daa3997f 100644 --- a/py/tox.ini +++ b/py/tox.ini @@ -25,17 +25,6 @@ deps = trio-typing==0.7.0 commands = mypy --install-types {posargs} - -[isort] -; isort is a common python tool for keeping imports nicely formatted. -; Automatically keep imports alphabetically sorted, on single lines in -; PEP recommended sections (https://peps.python.org/pep-0008/#imports) -; files or individual lines can be ignored via `# isort:skip|# isort:skip_file`. -profile = black -py_version=38 -force_single_line = True - - [testenv:linting-ci] ; checks linting for CI with stricter exiting when failing. skip_install = true diff --git a/rb/.rubocop.yml b/rb/.rubocop.yml index 14142a7db62a8..0e1f819a1d856 100644 --- a/rb/.rubocop.yml +++ b/rb/.rubocop.yml @@ -4,7 +4,7 @@ require: - rubocop-rspec AllCops: - TargetRubyVersion: 3.0 + TargetRubyVersion: 3.1 NewCops: enable Exclude: - !ruby/regexp /lib\/selenium\/devtools\/v\d+/ diff --git a/rb/.ruby-version b/rb/.ruby-version index 818bd47abfc91..9cec7165ab0a0 100644 --- a/rb/.ruby-version +++ b/rb/.ruby-version @@ -1 +1 @@ -3.0.6 +3.1.6 diff --git a/rb/CHANGES b/rb/CHANGES index 2711e0ada7c2d..00357fb0c0c52 100644 --- a/rb/CHANGES +++ b/rb/CHANGES @@ -1,3 +1,10 @@ +4.26.0 (2024-10-28) +========================= +* Add CDP for Chrome 130 and remove 127 +* Add missing RBS methods (#14621) +* Update Ruby BiDi script structs to match spec +* Add RBS type support for BiDi related classes (#14611) + 4.25.0 (2024-09-19) ========================= * Add CDP for Chrome 129 and remove 126 diff --git a/rb/Gemfile.lock b/rb/Gemfile.lock index 446dbdb16d988..4aedfe0ed9f98 100644 --- a/rb/Gemfile.lock +++ b/rb/Gemfile.lock @@ -1,9 +1,9 @@ PATH remote: . specs: - selenium-devtools (0.129.0) + selenium-devtools (0.130.0) selenium-webdriver (~> 4.2) - selenium-webdriver (4.26.0.nightly) + selenium-webdriver (4.27.0.nightly) base64 (~> 0.2) logger (~> 1.4) rexml (~> 3.2, >= 3.2.5) @@ -13,7 +13,7 @@ PATH GEM remote: https://rubygems.org/ specs: - activesupport (7.2.1) + activesupport (7.2.1.2) base64 bigdecimal concurrent-ruby (~> 1.0, >= 1.3.1) @@ -54,12 +54,12 @@ GEM concurrent-ruby (~> 1.0) io-console (0.7.2) io-console (0.7.2-java) - irb (1.14.0) + irb (1.14.1) rdoc (>= 4.0.0) reline (>= 0.4.2) jar-dependencies (0.4.1) - json (2.7.2) - json (2.7.2-java) + json (2.7.5) + json (2.7.5-java) language_server-protocol (3.17.0.3) listen (3.9.0) rb-fsevent (~> 0.10, >= 0.10.3) @@ -77,13 +77,13 @@ GEM public_suffix (6.0.1) racc (1.8.1) racc (1.8.1-java) - rack (2.2.9) + rack (2.2.10) rainbow (3.1.1) rake (13.2.1) rb-fsevent (0.11.2) rb-inotify (0.11.1) ffi (~> 1.0) - rbs (3.5.3) + rbs (3.6.1) logger rchardet (1.8.0) rdoc (6.7.0) @@ -91,21 +91,21 @@ GEM regexp_parser (2.9.2) reline (0.5.10) io-console (~> 0.5) - rexml (3.3.7) + rexml (3.3.9) rspec (3.13.0) rspec-core (~> 3.13.0) rspec-expectations (~> 3.13.0) rspec-mocks (~> 3.13.0) - rspec-core (3.13.1) + rspec-core (3.13.2) rspec-support (~> 3.13.0) rspec-expectations (3.13.3) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) - rspec-mocks (3.13.1) + rspec-mocks (3.13.2) diff-lcs (>= 1.2.0, < 2.0) rspec-support (~> 3.13.0) rspec-support (3.13.1) - rubocop (1.66.1) + rubocop (1.67.0) json (~> 2.3) language_server-protocol (>= 3.17.0) parallel (~> 1.10) @@ -115,7 +115,7 @@ GEM rubocop-ast (>= 1.32.2, < 2.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 2.4.0, < 3.0) - rubocop-ast (1.32.3) + rubocop-ast (1.33.0) parser (>= 3.3.1.0) rubocop-capybara (2.21.0) rubocop (~> 1.41) @@ -159,11 +159,11 @@ GEM tzinfo (2.0.6) concurrent-ruby (~> 1.0) unicode-display_width (2.6.0) - webmock (3.23.1) + webmock (3.24.0) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) - webrick (1.8.1) + webrick (1.8.2) websocket (1.2.11) yard (0.9.37) @@ -196,4 +196,4 @@ DEPENDENCIES yard (~> 0.9.11, >= 0.9.36) BUNDLED WITH - 2.5.14 + 2.5.6 diff --git a/rb/README.md b/rb/README.md index 651b47dc6828a..aacb9db114b10 100644 --- a/rb/README.md +++ b/rb/README.md @@ -1,6 +1,6 @@ # selenium-webdriver -This gem provides Ruby bindings for Selenium and supports MRI >= 3.0. +This gem provides Ruby bindings for Selenium and supports MRI >= 3.1. ## Install diff --git a/rb/lib/selenium/devtools/BUILD.bazel b/rb/lib/selenium/devtools/BUILD.bazel index 8aa9b187bdae8..c974adf9ba317 100644 --- a/rb/lib/selenium/devtools/BUILD.bazel +++ b/rb/lib/selenium/devtools/BUILD.bazel @@ -7,7 +7,7 @@ CDP_VERSIONS = [ "v85", "v128", "v129", - "v127", + "v130", ] rb_library( diff --git a/rb/lib/selenium/devtools/version.rb b/rb/lib/selenium/devtools/version.rb index be6d000d557c4..d75521bb415aa 100644 --- a/rb/lib/selenium/devtools/version.rb +++ b/rb/lib/selenium/devtools/version.rb @@ -19,6 +19,6 @@ module Selenium module DevTools - VERSION = '0.129.0' + VERSION = '0.130.0' end # DevTools end # Selenium diff --git a/rb/lib/selenium/server.rb b/rb/lib/selenium/server.rb index 7d031f26af07d..ad63ca25eb936 100644 --- a/rb/lib/selenium/server.rb +++ b/rb/lib/selenium/server.rb @@ -122,15 +122,15 @@ def available_assets end end - def net_http_start(address, &block) + def net_http_start(address, &) http_proxy = ENV.fetch('http_proxy', nil) || ENV.fetch('HTTP_PROXY', nil) if http_proxy http_proxy = "http://#{http_proxy}" unless http_proxy.start_with?('http://') uri = URI.parse(http_proxy) - Net::HTTP.start(address, nil, uri.host, uri.port, &block) + Net::HTTP.start(address, nil, uri.host, uri.port, &) else - Net::HTTP.start(address, use_ssl: true, &block) + Net::HTTP.start(address, use_ssl: true, &) end end diff --git a/rb/lib/selenium/webdriver/bidi.rb b/rb/lib/selenium/webdriver/bidi.rb index 0beb1d024578a..500f75859bc37 100644 --- a/rb/lib/selenium/webdriver/bidi.rb +++ b/rb/lib/selenium/webdriver/bidi.rb @@ -38,8 +38,8 @@ def callbacks @ws.callbacks end - def add_callback(event, &block) - @ws.add_callback(event, &block) + def add_callback(event, &) + @ws.add_callback(event, &) end def remove_callback(event, id) diff --git a/rb/lib/selenium/webdriver/bidi/log_inspector.rb b/rb/lib/selenium/webdriver/bidi/log_inspector.rb index 133666cec0f01..4c7ff02100a72 100644 --- a/rb/lib/selenium/webdriver/bidi/log_inspector.rb +++ b/rb/lib/selenium/webdriver/bidi/log_inspector.rb @@ -79,7 +79,7 @@ def on_javascript_exception(&block) end end - def on_log(filter_by = nil, &block) + def on_log(filter_by = nil, &) unless filter_by.nil? check_valid_filter(filter_by) @@ -89,14 +89,14 @@ def on_log(filter_by = nil, &block) return end - on(:entry_added, &block) + on(:entry_added, &) end private - def on(event, &block) + def on(event, &) event = EVENTS[event] if event.is_a?(Symbol) - @bidi.add_callback("log.#{event}", &block) + @bidi.add_callback("log.#{event}", &) end def check_valid_filter(filter_by) diff --git a/rb/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb b/rb/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb index 0cedd98ba3b52..3339426157d91 100644 --- a/rb/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb +++ b/rb/lib/selenium/webdriver/common/driver_extensions/has_network_interception.rb @@ -59,9 +59,9 @@ module HasNetworkInterception # @yieldparam [Proc] continue block which proceeds with the request and optionally yields response # - def intercept(&block) + def intercept(&) @interceptor ||= DevTools::NetworkInterceptor.new(devtools) - @interceptor.intercept(&block) + @interceptor.intercept(&) end end # HasNetworkInterception end # DriverExtensions diff --git a/rb/lib/selenium/webdriver/common/logger.rb b/rb/lib/selenium/webdriver/common/logger.rb index 318d4e3110ab5..810b87a8e3c31 100644 --- a/rb/lib/selenium/webdriver/common/logger.rb +++ b/rb/lib/selenium/webdriver/common/logger.rb @@ -193,7 +193,7 @@ def create_logger(name, level:) def discard_or_log(level, message, id) id = Array(id) - return if (@ignored & id).any? + return if @ignored.intersect?(id) return if @allowed.any? && (@allowed & id).none? return if ::Logger::Severity.const_get(level.upcase) < @logger.level diff --git a/rb/lib/selenium/webdriver/common/script.rb b/rb/lib/selenium/webdriver/common/script.rb index 4b58b1bbea2c7..a637b75f6bda1 100644 --- a/rb/lib/selenium/webdriver/common/script.rb +++ b/rb/lib/selenium/webdriver/common/script.rb @@ -25,13 +25,13 @@ def initialize(bridge) end # @return [int] id of the handler - def add_console_message_handler(&block) - @log_handler.add_message_handler('console', &block) + def add_console_message_handler(&) + @log_handler.add_message_handler('console', &) end # @return [int] id of the handler - def add_javascript_error_handler(&block) - @log_handler.add_message_handler('javascript', &block) + def add_javascript_error_handler(&) + @log_handler.add_message_handler('javascript', &) end # @param [int] id of the handler previously added diff --git a/rb/lib/selenium/webdriver/remote/bridge.rb b/rb/lib/selenium/webdriver/remote/bridge.rb index a26bb4bd1fe22..1678065010108 100644 --- a/rb/lib/selenium/webdriver/remote/bridge.rb +++ b/rb/lib/selenium/webdriver/remote/bridge.rb @@ -35,10 +35,10 @@ class << self attr_reader :extra_commands attr_writer :element_class, :locator_converter - def add_command(name, verb, url, &block) + def add_command(name, verb, url, &) @extra_commands ||= {} @extra_commands[name] = [verb, url] - define_method(name, &block) + define_method(name, &) end def locator_converter diff --git a/rb/lib/selenium/webdriver/support/guards.rb b/rb/lib/selenium/webdriver/support/guards.rb index ad5e1f2c9efd8..f56850106396d 100644 --- a/rb/lib/selenium/webdriver/support/guards.rb +++ b/rb/lib/selenium/webdriver/support/guards.rb @@ -37,8 +37,8 @@ def initialize(example, bug_tracker: '', conditions: nil) @messages = {} end - def add_condition(name, condition = nil, &blk) - @guard_conditions << GuardCondition.new(name, condition, &blk) + def add_condition(name, condition = nil, &) + @guard_conditions << GuardCondition.new(name, condition, &) end def add_message(name, message) diff --git a/rb/lib/selenium/webdriver/version.rb b/rb/lib/selenium/webdriver/version.rb index e99cd42fa460c..3a8d951f55562 100644 --- a/rb/lib/selenium/webdriver/version.rb +++ b/rb/lib/selenium/webdriver/version.rb @@ -19,6 +19,6 @@ module Selenium module WebDriver - VERSION = '4.26.0.nightly' + VERSION = '4.27.0.nightly' end # WebDriver end # Selenium diff --git a/rb/selenium-devtools.gemspec b/rb/selenium-devtools.gemspec index 455966163f019..709efc6bcdb2c 100644 --- a/rb/selenium-devtools.gemspec +++ b/rb/selenium-devtools.gemspec @@ -27,7 +27,7 @@ Gem::Specification.new do |s| } s.required_rubygems_version = Gem::Requirement.new('> 1.3.1') if s.respond_to? :required_rubygems_version= - s.required_ruby_version = Gem::Requirement.new('>= 3.0') + s.required_ruby_version = Gem::Requirement.new('>= 3.1') s.files = [ 'LICENSE', diff --git a/rb/selenium-webdriver.gemspec b/rb/selenium-webdriver.gemspec index 64a92dcb358fc..b6c09f273c326 100644 --- a/rb/selenium-webdriver.gemspec +++ b/rb/selenium-webdriver.gemspec @@ -28,7 +28,7 @@ Gem::Specification.new do |s| } s.required_rubygems_version = Gem::Requirement.new('> 1.3.1') if s.respond_to? :required_rubygems_version= - s.required_ruby_version = Gem::Requirement.new('>= 3.0') + s.required_ruby_version = Gem::Requirement.new('>= 3.1') s.files = [ 'CHANGES', diff --git a/rb/spec/integration/selenium/webdriver/remote/driver_spec.rb b/rb/spec/integration/selenium/webdriver/remote/driver_spec.rb index 36038859cd303..671aa1720fef3 100644 --- a/rb/spec/integration/selenium/webdriver/remote/driver_spec.rb +++ b/rb/spec/integration/selenium/webdriver/remote/driver_spec.rb @@ -83,10 +83,12 @@ module Remote it 'errors when not set', {except: {browser: :firefox, reason: 'grid always sets true and firefox returns it'}, exclude: {browser: :safari, reason: 'grid hangs'}} do - expect { - driver.downloadable_files - }.to raise_exception(Error::WebDriverError, - 'You must enable downloads in order to work with downloadable files.') + reset_driver!(enable_downloads: false) do |driver| + expect { + driver.downloadable_files + }.to raise_exception(Error::WebDriverError, + 'You must enable downloads in order to work with downloadable files.') + end end private diff --git a/rust/CHANGELOG.md b/rust/CHANGELOG.md index b56d9cf5b207f..12049809f5d18 100644 --- a/rust/CHANGELOG.md +++ b/rust/CHANGELOG.md @@ -1,3 +1,7 @@ +0.4.26 +====== +* Selenium Manager checks invalid browser version (#14511) + 0.4.25 ====== diff --git a/scripts/github-actions/release_header.md b/scripts/github-actions/release_header.md index 357a1461dd907..f56da16681f6b 100644 --- a/scripts/github-actions/release_header.md +++ b/scripts/github-actions/release_header.md @@ -1,4 +1,5 @@ ## Detailed Changelogs by Component - **[Java](https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG)**     |     **[Python](https://github.com/SeleniumHQ/selenium/blob/trunk/py/CHANGES)**     |     **[DotNet](https://github.com/SeleniumHQ/selenium/blob/trunk/dotnet/CHANGELOG)**     |     **[Ruby](https://github.com/SeleniumHQ/selenium/blob/trunk/rb/CHANGES)**     |     **[JavaScript](https://github.com/SeleniumHQ/selenium/blob/trunk/javascript/node/selenium-webdriver/CHANGES.md)**     |     **[IEDriver](https://github.com/SeleniumHQ/selenium/blob/trunk/cpp/iedriverserver/CHANGELOG)** + **[Java](https://github.com/SeleniumHQ/selenium/blob/trunk/java/CHANGELOG)**     |     **[Python](https://github.com/SeleniumHQ/selenium/blob/trunk/py/CHANGES)**     |     **[DotNet](https://github.com/SeleniumHQ/selenium/blob/trunk/dotnet/CHANGELOG)**     |     **[Ruby](https://github.com/SeleniumHQ/selenium/blob/trunk/rb/CHANGES)**     |     **[JavaScript](https://github.com/SeleniumHQ/selenium/blob/trunk/javascript/node/selenium-webdriver/CHANGES.md)**     |     **[IEDriver](https://github.com/SeleniumHQ/selenium/blob/trunk/cpp/iedriverserver/CHANGELOG)**
+ diff --git a/scripts/pinned_browsers.py b/scripts/pinned_browsers.py index 70be06421e5f9..b41b169577f46 100755 --- a/scripts/pinned_browsers.py +++ b/scripts/pinned_browsers.py @@ -261,12 +261,15 @@ def edge(): def edgedriver(): - r = http.request("GET", "https://msedgedriver.azureedge.net/LATEST_STABLE") - v = r.data.decode("utf-16").strip() + r_stable = http.request("GET", "https://msedgedriver.azureedge.net/LATEST_STABLE") + stable_version = r_stable.data.decode("utf-16").strip() + major_version = stable_version.split('.')[0] + r = http.request("GET", f"https://msedgedriver.azureedge.net/LATEST_RELEASE_{major_version}_LINUX") + linux_version = r.data.decode("utf-16").strip() content = "" - linux = "https://msedgedriver.azureedge.net/%s/edgedriver_linux64.zip" % v + linux = "https://msedgedriver.azureedge.net/%s/edgedriver_linux64.zip" % linux_version sha = calculate_hash(linux) content = ( content @@ -291,7 +294,9 @@ def edgedriver(): % (linux, sha) ) - mac = "https://msedgedriver.azureedge.net/%s/edgedriver_mac64.zip" % v + r = http.request("GET", f"https://msedgedriver.azureedge.net/LATEST_RELEASE_{major_version}_MACOS") + macos_version = r.data.decode("utf-16").strip() + mac = "https://msedgedriver.azureedge.net/%s/edgedriver_mac64.zip" % macos_version sha = calculate_hash(mac) content = ( content