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-dotnet.yml b/.github/workflows/ci-dotnet.yml index 65debef8c10e4..1e2a2c259382d 100644 --- a/.github/workflows/ci-dotnet.yml +++ b/.github/workflows/ci-dotnet.yml @@ -23,4 +23,5 @@ jobs: java-version: 17 os: windows run: | + fsutil 8dot3name set 0 bazel test //dotnet/test/common:ElementFindingTest-firefox //dotnet/test/common:ElementFindingTest-chrome --pin_browsers=true diff --git a/.github/workflows/ci-java.yml b/.github/workflows/ci-java.yml index 569d68d562440..1ebd054c05089 100644 --- a/.github/workflows/ci-java.yml +++ b/.github/workflows/ci-java.yml @@ -22,6 +22,7 @@ jobs: # https://github.com/bazelbuild/rules_jvm_external/issues/1046 java-version: 17 run: | + fsutil 8dot3name set 0 bazel test --flaky_test_attempts 3 //java/test/org/openqa/selenium/chrome:ChromeDriverFunctionalTest ` //java/test/org/openqa/selenium/federatedcredentialmanagement:FederatedCredentialManagementTest ` //java/test/org/openqa/selenium/firefox:FirefoxDriverBuilderTest ` diff --git a/.github/workflows/update-documentation.yml b/.github/workflows/update-documentation.yml index 7e2e5efee541f..4bed149651701 100644 --- a/.github/workflows/update-documentation.yml +++ b/.github/workflows/update-documentation.yml @@ -46,7 +46,7 @@ jobs: git config --local user.email "selenium-ci@users.noreply.github.com" git config --local user.name "Selenium CI Bot" - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'temurin' @@ -58,7 +58,7 @@ jobs: git format-patch -1 HEAD --stdout > java-docs.patch - name: Upload patch if: needs.determine-language.outputs.language == 'all' || needs.determine-language.outputs.language == 'java' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: java-docs-patch path: java-docs.patch @@ -76,10 +76,12 @@ jobs: git config --local user.email "selenium-ci@users.noreply.github.com" git config --local user.name "Selenium CI Bot" - name: Setup Java - uses: actions/setup-java@v3 + uses: actions/setup-java@v4 with: java-version: 17 distribution: 'temurin' + - name: Setup curl for Ubuntu + run: sudo apt-get update && sudo apt-get install -y libcurl4-openssl-dev - name: Update Documentation if: needs.determine-language.outputs.language == 'all' || needs.determine-language.outputs.language == 'ruby' run: ./go rb:docs @@ -88,7 +90,7 @@ jobs: git format-patch -1 HEAD --stdout > ruby-docs.patch - name: Upload patch if: needs.determine-language.outputs.language == 'all' || needs.determine-language.outputs.language == 'ruby' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: ruby-docs-patch path: ruby-docs.patch @@ -106,7 +108,7 @@ jobs: git config --local user.email "selenium-ci@users.noreply.github.com" git config --local user.name "Selenium CI Bot" - name: Set up Python 3.8 - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 - name: Install dependencies @@ -121,7 +123,7 @@ jobs: git format-patch -1 HEAD --stdout > python-docs.patch - name: Upload patch if: needs.determine-language.outputs.language == 'all' || needs.determine-language.outputs.language == 'python' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: python-docs-patch path: python-docs.patch @@ -150,7 +152,7 @@ jobs: git format-patch -1 HEAD --stdout > dotnet-docs.patch - name: Upload patch if: needs.determine-language.outputs.language == 'all' || needs.determine-language.outputs.language == 'dotnet' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: dotnet-docs-patch path: dotnet-docs.patch @@ -179,7 +181,7 @@ jobs: git format-patch -1 HEAD --stdout > node-docs.patch - name: Upload patch if: needs.determine-language.outputs.language == 'all' || needs.determine-language.outputs.language == 'node' - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: node-docs-patch path: node-docs.patch @@ -192,46 +194,26 @@ jobs: uses: actions/checkout@v4 with: ref: 'gh-pages' + fetch-depth: 0 + fetch-tags: true - name: Create and checkout new branch run: | git config --local user.email "selenium-ci@users.noreply.github.com" git config --local user.name "Selenium CI Bot" git checkout -b api-docs-${{ inputs.tag }} - - name: Download Java patch - if: needs.determine-language.outputs.language == 'all' || needs.determine-language.outputs.language == 'java' - uses: actions/download-artifact@v2 - with: - name: java-docs-patch - path: patches/ - - name: Download Ruby patch - if: needs.determine-language.outputs.language == 'all' || needs.determine-language.outputs.language == 'ruby' - uses: actions/download-artifact@v2 - with: - name: ruby-docs-patch - path: patches/ - - name: Download Python patch - if: needs.determine-language.outputs.language == 'all' || needs.determine-language.outputs.language == 'python' - uses: actions/download-artifact@v2 + - name: Download all patches + uses: actions/download-artifact@v4 with: - name: python-docs-patch - path: patches/ - - name: Download .NET patch - if: needs.determine-language.outputs.language == 'all' || needs.determine-language.outputs.language == 'dotnet' - uses: actions/download-artifact@v2 - with: - name: dotnet-docs-patch - path: patches/ - - name: Download Node patch - if: needs.determine-language.outputs.language == 'all' || needs.determine-language.outputs.language == 'node' - uses: actions/download-artifact@v2 - with: - name: node-docs-patch path: patches/ + merge-multiple: true + - name: Show downloaded patches + run: ls -lah patches/ - name: Apply patches run: | for patch in patches/*.patch; do git am < "$patch" done + rm -rf patches/ - name: Push Branch run: git push origin api-docs-${{ inputs.tag }} - name: Documentation Pull Request 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 348c66a2bf10d..a1b464caee2c5 100644 --- a/MODULE.bazel +++ b/MODULE.bazel @@ -17,10 +17,10 @@ bazel_dep(name = "protobuf", version = "21.7", dev_dependency = True, repo_name # Required for rules_rust to import the crates properly bazel_dep(name = "rules_cc", version = "0.0.9", dev_dependency = True) -bazel_dep(name = "rules_dotnet", version = "0.15.1") +bazel_dep(name = "rules_dotnet", version = "0.16.0") bazel_dep(name = "rules_java", version = "7.11.1") bazel_dep(name = "rules_jvm_external", version = "6.3") -bazel_dep(name = "rules_nodejs", version = "6.2.0") +bazel_dep(name = "rules_nodejs", version = "6.3.0") 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") @@ -76,7 +76,7 @@ rules_ts_ext.deps( use_repo(rules_ts_ext, "npm_typescript") esbuild = use_extension("@aspect_rules_esbuild//esbuild:extensions.bzl", "esbuild") -esbuild.toolchain(esbuild_version = "0.19.9") +esbuild.toolchain(esbuild_version = "0.23.0") use_repo(esbuild, "esbuild_toolchains") register_toolchains("@esbuild_toolchains//:all") @@ -173,53 +173,52 @@ maven.install( "com.github.spotbugs:spotbugs:4.8.6", "com.github.stephenc.jcip:jcip-annotations:1.0-1", "com.google.code.gson:gson:2.11.0", - "com.google.guava:guava:33.3.0-jre", + "com.google.guava:guava:33.3.1-jre", "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.graphql-java:graphql-java:20.2", - "com.graphql-java:java-dataloader:3.2.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", diff --git a/README.md b/README.md index e5ef14de8c4f4..637c6428c3198 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,7 @@ # Selenium [![CI](https://github.com/SeleniumHQ/selenium/actions/workflows/ci.yml/badge.svg?branch=trunk&event=schedule)](https://github.com/SeleniumHQ/selenium/actions/workflows/ci.yml) +[![Releases downloads](https://img.shields.io/github/downloads/SeleniumHQ/selenium/total.svg)](https://github.com/SeleniumHQ/selenium/releases) Selenium Logo diff --git a/Rakefile b/Rakefile index 5c2ca0fe42ccb..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 @@ -483,7 +483,6 @@ namespace :node do new_version = updated_version(old_version, arguments[:version], nightly) ['javascript/node/selenium-webdriver/package.json', - 'package-lock.json', 'javascript/node/selenium-webdriver/BUILD.bazel'].each do |file| text = File.read(file).gsub(old_version, new_version) File.open(file, 'w') { |f| f.puts text } @@ -792,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' @@ -1050,8 +1049,7 @@ namespace :all do 'py/BUILD.bazel', 'py/setup.py', 'rb/lib/selenium/webdriver/version.rb', - 'rb/Gemfile.lock', - 'package-lock.json']) + 'rb/Gemfile.lock']) print 'Do you want to push the committed changes? (Y/n): ' response = $stdin.gets.chomp.downcase @@ -1106,7 +1104,6 @@ namespace :all do 'java/version.bzl', 'javascript/node/selenium-webdriver/CHANGES.md', 'javascript/node/selenium-webdriver/package.json', - 'package-lock.json', 'py/docs/source/conf.py', 'py/selenium/__init__.py', 'py/selenium/webdriver/__init__.py', diff --git a/WORKSPACE b/WORKSPACE index eb09ccc8726e0..98cec87d366b7 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -34,8 +34,8 @@ rules_closure_toolchains() http_archive( name = "rules_rust", - integrity = "sha256-JLN47ZcAbx9wEr5Jiib4HduZATGLiDgK7oUi/fvotzU=", - urls = ["https://github.com/bazelbuild/rules_rust/releases/download/0.42.1/rules_rust-v0.42.1.tar.gz"], + integrity = "sha256-Zx3bP+Xrz53TTQUeynNS+68z+lO/Ye7Qt1pMNIKeVIA=", + urls = ["https://github.com/bazelbuild/rules_rust/releases/download/0.52.2/rules_rust-v0.52.2.tar.gz"], ) load("@rules_rust//rust:repositories.bzl", "rules_rust_dependencies", "rust_register_toolchains") 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/extensions/webextensions-selenium-example.crx b/common/extensions/webextensions-selenium-example.crx index 38b38003b7ec3..941114eb446e8 100644 Binary files a/common/extensions/webextensions-selenium-example.crx and b/common/extensions/webextensions-selenium-example.crx differ diff --git a/common/extensions/webextensions-selenium-example/manifest.json b/common/extensions/webextensions-selenium-example/manifest.json index 60f2e5460c500..69e480dc60dda 100644 --- a/common/extensions/webextensions-selenium-example/manifest.json +++ b/common/extensions/webextensions-selenium-example/manifest.json @@ -14,6 +14,14 @@ ] } ], + "permissions": [ + "storage", + "scripting" + ], + "host_permissions": [ + "https://*/*", + "http://*/*" + ], "browser_specific_settings": { "gecko": { "id": "webextensions-selenium-example-v3@example.com" diff --git a/common/mirror/selenium b/common/mirror/selenium index eeffcbd977915..5c1e9a15d6f24 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.25.0.zip" + "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-server-4.25.0.jar" + "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.25.0.zip" + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/nightly/selenium-server-4.26.0-SNAPSHOT.zip" + } + ] + }, + { + "tag_name": "selenium-4.25.0", + "assets": [ + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.25.0/selenium-dotnet-4.25.0.zip" + }, + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.25.0/selenium-dotnet-strongnamed-4.25.0.zip" + }, + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.25.0/selenium-java-4.25.0.zip" + }, + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.25.0/selenium-server-4.25.0.jar" + }, + { + "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.25.0/selenium-server-4.25.0.zip" } ] }, @@ -938,25 +958,5 @@ "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-rc-1/selenium-server-4.0.0-rc-1.zip" } ] - }, - { - "tag_name": "selenium-4.0.0-beta-4", - "assets": [ - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-beta-4/selenium-dotnet-4.0.0-beta-4.zip" - }, - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-beta-4/selenium-dotnet-strongnamed-4.0.0-beta-4.zip" - }, - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-beta-4/selenium-html-runner-4.0.0-beta-4.jar" - }, - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-beta-4/selenium-java-4.0.0-beta-4.zip" - }, - { - "browser_download_url": "https://github.com/SeleniumHQ/selenium/releases/download/selenium-4.0.0-beta-4/selenium-server-4.0.0-beta-4.jar" - } - ] } ] diff --git a/common/repositories.bzl b/common/repositories.bzl index 2b68a39f8cd05..5003c5dd480ca 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/130.0.1/linux-x86_64/en-US/firefox-130.0.1.tar.bz2", - sha256 = "3b9cd7fe7d22f0960ee5a058e3c9e6b507814958ec5a9ac691cbd5ebd0895c93", + 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/130.0.1/mac/en-US/Firefox%20130.0.1.dmg", - sha256 = "63ed878485d5498c269d95ba7e64f1104ed085b8e330b0ef0a565f85cc603426", + 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/131.0b8/linux-x86_64/en-US/firefox-131.0b8.tar.bz2", - sha256 = "53a6c775688243908d5d010c9c14eda488d20faabb966be31fd49d711b89f1e1", + url = "https://ftp.mozilla.org/pub/firefox/releases/133.0b1/linux-x86_64/en-US/firefox-133.0b1.tar.bz2", + sha256 = "e61434729b6c0be2cc1d27b6c1847f54c1b228a49a4480260de1e10ca2a115c2", 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/131.0b8/mac/en-US/Firefox%20131.0b8.dmg", - sha256 = "8acb3265bdf9859abfbb58604f5b253a0efe1fab7b82d4ae7d3983706ac5427b", + url = "https://ftp.mozilla.org/pub/firefox/releases/133.0b1/mac/en-US/Firefox%20133.0b1.dmg", + sha256 = "999529fe32c8630c234b6cff09a204a1c178ca8fc1a4e17c66097b943a87ebc4", 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/28b1932d-413c-4868-b79f-f72482800efb/MicrosoftEdge-128.0.2739.79.pkg", - sha256 = "8bcdd29a37414136e46860acb6c151d3ae1f9ef1ee62464a0d00a7eda71b5e29", + 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-128.0.2739.79.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_128.0.2739.79-1_amd64.deb", - sha256 = "6238024b9d240927b76bb199021fe6e804d04759b61992638871755f5cc444f5", + 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/128.0.2739.81/edgedriver_linux64.zip", - sha256 = "66a7b7fca41920c813263642ee5be105bb3b18f89fa09319a3b681ccf608aeb5", + url = "https://msedgedriver.azureedge.net/130.0.2849.56/edgedriver_linux64.zip", + sha256 = "c5aa82d87750a6f49b741790a432194ff91d0a232c54eb5507a526f45146f440", 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/128.0.2739.81/edgedriver_mac64.zip", - sha256 = "554b60f5863f13db237bbba75fa8bac52517c364104dc859df0b457804ed10c9", + url = "https://msedgedriver.azureedge.net/130.0.2849.56/edgedriver_mac64.zip", + sha256 = "97141db0a53b22356094ff4d7a806a19ab816499366539e633a32fc4af8da2a3", 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.58/linux64/chrome-linux64.zip", - sha256 = "0918cb647b186f6e429a90e67a716489cf96d0295da8a3a8c40a441379708ba9", + 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.58/mac-x64/chrome-mac-x64.zip", - sha256 = "2f1c317cde0ba19be2f4e731cb77fc0b45ec423781c099ae5208228abedd1359", + 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.58/linux64/chromedriver-linux64.zip", - sha256 = "0fc68a18a9db153e98521ca1654bb3fd7842ad4552f98ec8ec6688e907efd9b1", + 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.58/mac-x64/chromedriver-mac-x64.zip", - sha256 = "df4df07aa534e1c082c252e54959c4caf2af1eed3d4631852e683971ffdec6cb", + 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..70c0383aba8d9 100644 --- a/dotnet/CHANGELOG +++ b/dotnet/CHANGELOG @@ -1,3 +1,17 @@ +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 679825b8a64ec..d4ecd22884cf1 100644 --- a/dotnet/selenium-dotnet-version.bzl +++ b/dotnet/selenium-dotnet-version.bzl @@ -1,6 +1,6 @@ # BUILD FILE SYNTAX: STARLARK -SE_VERSION = "4.25.0" +SE_VERSION = "4.26.0" 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/support/UI/LoadableComponentException.cs b/dotnet/src/support/UI/LoadableComponentException.cs index 64354a240ac12..305543f50a3cd 100644 --- a/dotnet/src/support/UI/LoadableComponentException.cs +++ b/dotnet/src/support/UI/LoadableComponentException.cs @@ -57,17 +57,5 @@ public LoadableComponentException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected LoadableComponentException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/support/UI/UnexpectedTagNameException.cs b/dotnet/src/support/UI/UnexpectedTagNameException.cs index 9cea8a3781e2b..9fc85a8f39bb7 100644 --- a/dotnet/src/support/UI/UnexpectedTagNameException.cs +++ b/dotnet/src/support/UI/UnexpectedTagNameException.cs @@ -69,17 +69,5 @@ public UnexpectedTagNameException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected UnexpectedTagNameException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/BUILD.bazel b/dotnet/src/webdriver/BUILD.bazel index 1f23755fa1025..afcdd0785bae4 100644 --- a/dotnet/src/webdriver/BUILD.bazel +++ b/dotnet/src/webdriver/BUILD.bazel @@ -55,6 +55,7 @@ csharp_library( framework("nuget", "Microsoft.Bcl.AsyncInterfaces"), framework("nuget", "System.Threading.Tasks.Extensions"), framework("nuget", "System.Memory"), + framework("nuget", "System.Text.Encodings.Web"), framework("nuget", "System.Text.Json"), ], ) @@ -119,6 +120,7 @@ csharp_library( framework("nuget", "Microsoft.Bcl.AsyncInterfaces"), framework("nuget", "System.Threading.Tasks.Extensions"), framework("nuget", "System.Memory"), + framework("nuget", "System.Text.Encodings.Web"), framework("nuget", "System.Text.Json"), ], ) diff --git a/dotnet/src/webdriver/BiDi/BiDi.cs b/dotnet/src/webdriver/BiDi/BiDi.cs index db53571e8ad8c..34a9452dec144 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; + internal 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 f8a76f208a8d0..76384f51b1261 100644 --- a/dotnet/src/webdriver/BiDi/Communication/Transport/WebSocketTransport.cs +++ b/dotnet/src/webdriver/BiDi/Communication/Transport/WebSocketTransport.cs @@ -7,18 +7,21 @@ 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(); private readonly ClientWebSocket _webSocket = new(); private readonly ArraySegment _receiveBuffer = new(new byte[1024 * 8]); + private readonly SemaphoreSlim _socketSendSemaphoreSlim = new(1, 1); + public async Task ConnectAsync(CancellationToken cancellationToken) { - _webSocket.Options.SetBuffer(_receiveBuffer.Count, _receiveBuffer.Count, _receiveBuffer); await _webSocket.ConnectAsync(_uri, cancellationToken).ConfigureAwait(false); } @@ -32,8 +35,9 @@ public async Task ReceiveAsJsonAsync(JsonSerializerOptions jsonSerializerO { result = await _webSocket.ReceiveAsync(_receiveBuffer, cancellationToken).ConfigureAwait(false); - await ms.WriteAsync(_receiveBuffer.Array!, _receiveBuffer.Offset, result.Count).ConfigureAwait(false); - } while (!result.EndOfMessage); + await ms.WriteAsync(_receiveBuffer.Array!, _receiveBuffer.Offset, result.Count, cancellationToken).ConfigureAwait(false); + } + while (!result.EndOfMessage); ms.Seek(0, SeekOrigin.Begin); @@ -51,12 +55,21 @@ public async Task SendAsJsonAsync(Command command, JsonSerializerOptions jsonSer { var buffer = JsonSerializer.SerializeToUtf8Bytes(command, typeof(Command), jsonSerializerOptions); - if (_logger.IsEnabled(LogEventLevel.Trace)) + await _socketSendSemaphoreSlim.WaitAsync(cancellationToken); + + try { - _logger.Trace($"BiDi SND >> {buffer.Length} > {Encoding.UTF8.GetString(buffer)}"); - } + if (_logger.IsEnabled(LogEventLevel.Trace)) + { + _logger.Trace($"BiDi SND >> {Encoding.UTF8.GetString(buffer)}"); + } - await _webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, cancellationToken).ConfigureAwait(false); + await _webSocket.SendAsync(new ArraySegment(buffer), WebSocketMessageType.Text, true, cancellationToken).ConfigureAwait(false); + } + finally + { + _socketSendSemaphoreSlim.Release(); + } } public void Dispose() 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..409c393860d6c 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,34 @@ 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(Proxy))] + internal partial class CommandJsonSerializerContext : JsonSerializerContext + { + + } } diff --git a/dotnet/src/webdriver/DetachedShadowRootException.cs b/dotnet/src/webdriver/DetachedShadowRootException.cs index 43608ca8358d7..d8af8c148db6f 100644 --- a/dotnet/src/webdriver/DetachedShadowRootException.cs +++ b/dotnet/src/webdriver/DetachedShadowRootException.cs @@ -57,17 +57,5 @@ public DetachedShadowRootException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected DetachedShadowRootException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } 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/DevToolsSession.cs b/dotnet/src/webdriver/DevTools/DevToolsSession.cs index 825d31fe4fd7e..931fb22dce019 100644 --- a/dotnet/src/webdriver/DevTools/DevToolsSession.cs +++ b/dotnet/src/webdriver/DevTools/DevToolsSession.cs @@ -16,6 +16,7 @@ // limitations under the License. // +using OpenQA.Selenium.Internal.Logging; using System; using System.Collections.Concurrent; using System.Globalization; @@ -56,6 +57,8 @@ public class DevToolsSession : IDevToolsSession private DevToolsDomains domains; private readonly DevToolsOptions options; + private readonly static ILogger logger = Internal.Logging.Log.GetLogger(); + /// /// Initializes a new instance of the DevToolsSession class, using the specified WebSocket endpoint. /// @@ -272,6 +275,11 @@ public T GetVersionSpecificDomains() where T : DevToolsSessionDomains if (this.connection != null && this.connection.IsActive) { + if (logger.IsEnabled(LogEventLevel.Trace)) + { + logger.Trace($"CDP SND >> {message.CommandId} {message.CommandName}: {commandParameters.ToJsonString()}"); + } + LogTrace("Sending {0} {1}: {2}", message.CommandId, message.CommandName, commandParameters.ToString()); string contents = JsonSerializer.Serialize(message); @@ -301,7 +309,7 @@ public T GetVersionSpecificDomains() where T : DevToolsSessionDomains LogTrace("Recieved Error Response {0}: {1} {2}", modified.CommandId, message, errorData); throw new CommandResponseException(exceptionMessage) { - Code = modified.Result["code"].GetValue() + Code = modified.Result["code"]?.GetValue() ?? -1 }; } @@ -540,6 +548,11 @@ private void MonitorMessageQueue() private void ProcessMessage(string message) { + if (logger.IsEnabled(LogEventLevel.Trace)) + { + logger.Trace($"CDP RCV << {message}"); + } + var messageObject = JsonObject.Parse(message).AsObject(); if (messageObject.TryGetPropertyValue("id", out var idProperty)) @@ -583,7 +596,22 @@ private void ProcessMessage(string message) // DevTools commands that may be sent in the body of the attached // event handler. If thread pool starvation seems to become a problem, // we can switch to a channel-based queue. - Task.Run(() => OnDevToolsEventReceived(new DevToolsEventReceivedEventArgs(methodParts[0], methodParts[1], eventData))); + Task.Run(() => + { + try + { + OnDevToolsEventReceived(new DevToolsEventReceivedEventArgs(methodParts[0], methodParts[1], eventData)); + } + catch (Exception ex) + { + if (logger.IsEnabled(LogEventLevel.Warn)) + { + logger.Warn($"CDP VNT ^^ Unhandled error occured in event handler of '{method}' method. {ex}"); + } + + throw; + } + }); return; } 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/DriverService.cs b/dotnet/src/webdriver/DriverService.cs index a061bee8594cb..d3f864d3d396b 100644 --- a/dotnet/src/webdriver/DriverService.cs +++ b/dotnet/src/webdriver/DriverService.cs @@ -16,7 +16,6 @@ // limitations under the License. // -using OpenQA.Selenium.Internal.Logging; using OpenQA.Selenium.Remote; using System; using System.Diagnostics; @@ -42,7 +41,6 @@ public abstract class DriverService : ICommandServer private bool isDisposed; private Process driverServiceProcess; private TimeSpan initializationTimeout = TimeSpan.FromSeconds(20); - private readonly static ILogger logger = Log.GetLogger(); /// /// Initializes a new instance of the class. @@ -238,10 +236,7 @@ protected virtual bool IsInitialized } catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException) { - if (logger.IsEnabled(LogEventLevel.Trace)) - { - logger.Trace(ex.ToString()); - } + // Do nothing. The exception is expected, meaning driver service is not initialized. } return isInitialized; diff --git a/dotnet/src/webdriver/DriverServiceNotFoundException.cs b/dotnet/src/webdriver/DriverServiceNotFoundException.cs index 25ad25cb6d9bc..2094d61cc2761 100644 --- a/dotnet/src/webdriver/DriverServiceNotFoundException.cs +++ b/dotnet/src/webdriver/DriverServiceNotFoundException.cs @@ -57,17 +57,5 @@ public DriverServiceNotFoundException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected DriverServiceNotFoundException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/ElementClickInterceptedException.cs b/dotnet/src/webdriver/ElementClickInterceptedException.cs index 12d073ed66c0f..f937dc5e58af9 100644 --- a/dotnet/src/webdriver/ElementClickInterceptedException.cs +++ b/dotnet/src/webdriver/ElementClickInterceptedException.cs @@ -57,17 +57,5 @@ public ElementClickInterceptedException(string message, Exception innerException : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected ElementClickInterceptedException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/ElementNotInteractableException.cs b/dotnet/src/webdriver/ElementNotInteractableException.cs index f7ccf80ca5e5d..65ae5395e2207 100644 --- a/dotnet/src/webdriver/ElementNotInteractableException.cs +++ b/dotnet/src/webdriver/ElementNotInteractableException.cs @@ -57,17 +57,5 @@ public ElementNotInteractableException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected ElementNotInteractableException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/ElementNotSelectableException.cs b/dotnet/src/webdriver/ElementNotSelectableException.cs index 3574fc6671a77..68fa2b7f4ad6e 100644 --- a/dotnet/src/webdriver/ElementNotSelectableException.cs +++ b/dotnet/src/webdriver/ElementNotSelectableException.cs @@ -57,17 +57,5 @@ public ElementNotSelectableException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected ElementNotSelectableException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/ElementNotVisibleException.cs b/dotnet/src/webdriver/ElementNotVisibleException.cs index 8f27ec01cfa07..43f815ae105ad 100644 --- a/dotnet/src/webdriver/ElementNotVisibleException.cs +++ b/dotnet/src/webdriver/ElementNotVisibleException.cs @@ -57,17 +57,5 @@ public ElementNotVisibleException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected ElementNotVisibleException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/InsecureCertificateException.cs b/dotnet/src/webdriver/InsecureCertificateException.cs index d8cfd9bd71f16..37da31170bf21 100644 --- a/dotnet/src/webdriver/InsecureCertificateException.cs +++ b/dotnet/src/webdriver/InsecureCertificateException.cs @@ -57,17 +57,5 @@ public InsecureCertificateException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected InsecureCertificateException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } 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/InvalidCookieDomainException.cs b/dotnet/src/webdriver/InvalidCookieDomainException.cs index 3e6debb2d72a7..0b9294bb6d5bd 100644 --- a/dotnet/src/webdriver/InvalidCookieDomainException.cs +++ b/dotnet/src/webdriver/InvalidCookieDomainException.cs @@ -57,17 +57,5 @@ public InvalidCookieDomainException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected InvalidCookieDomainException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/InvalidElementStateException.cs b/dotnet/src/webdriver/InvalidElementStateException.cs index f0f2fed91c3a3..1010a9f084d8f 100644 --- a/dotnet/src/webdriver/InvalidElementStateException.cs +++ b/dotnet/src/webdriver/InvalidElementStateException.cs @@ -57,17 +57,5 @@ public InvalidElementStateException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected InvalidElementStateException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/InvalidSelectorException.cs b/dotnet/src/webdriver/InvalidSelectorException.cs index 9cdf2c3e66f17..509dc2d74a785 100644 --- a/dotnet/src/webdriver/InvalidSelectorException.cs +++ b/dotnet/src/webdriver/InvalidSelectorException.cs @@ -63,18 +63,6 @@ public InvalidSelectorException(string message, Exception innerException) { } - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected InvalidSelectorException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - /// /// Add information about obtaining additional support from documentation to this exception. /// diff --git a/dotnet/src/webdriver/JavaScriptException.cs b/dotnet/src/webdriver/JavaScriptException.cs index b852e904bfc5e..79045e4093c08 100644 --- a/dotnet/src/webdriver/JavaScriptException.cs +++ b/dotnet/src/webdriver/JavaScriptException.cs @@ -57,17 +57,5 @@ public JavaScriptException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected JavaScriptException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/Logs.cs b/dotnet/src/webdriver/Logs.cs index 4ad943d9b7a44..6c903dccc9efd 100644 --- a/dotnet/src/webdriver/Logs.cs +++ b/dotnet/src/webdriver/Logs.cs @@ -76,29 +76,23 @@ public ReadOnlyCollection AvailableLogTypes public ReadOnlyCollection GetLog(string logKind) { List entries = new List(); - try - { - Dictionary parameters = new Dictionary(); - parameters.Add("type", logKind); - Response commandResponse = this.driver.InternalExecute(DriverCommand.GetLog, parameters); - object[] responseValue = commandResponse.Value as object[]; - if (responseValue != null) + Dictionary parameters = new Dictionary(); + parameters.Add("type", logKind); + Response commandResponse = this.driver.InternalExecute(DriverCommand.GetLog, parameters); + + object[] responseValue = commandResponse.Value as object[]; + if (responseValue != null) + { + foreach (object rawEntry in responseValue) { - foreach (object rawEntry in responseValue) + Dictionary entryDictionary = rawEntry as Dictionary; + if (entryDictionary != null) { - Dictionary entryDictionary = rawEntry as Dictionary; - if (entryDictionary != null) - { - entries.Add(LogEntry.FromDictionary(entryDictionary)); - } + entries.Add(LogEntry.FromDictionary(entryDictionary)); } } } - catch (NotImplementedException) - { - // Swallow for backwards compatibility - } return entries.AsReadOnly(); } diff --git a/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs b/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs index 7394ce444715d..d2cbd4ea1f1c0 100644 --- a/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs +++ b/dotnet/src/webdriver/MoveTargetOutOfBoundsException.cs @@ -58,17 +58,5 @@ public MoveTargetOutOfBoundsException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected MoveTargetOutOfBoundsException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/NetworkManager.cs b/dotnet/src/webdriver/NetworkManager.cs index 8d91b72bc27b0..7d3131c7a6114 100644 --- a/dotnet/src/webdriver/NetworkManager.cs +++ b/dotnet/src/webdriver/NetworkManager.cs @@ -45,7 +45,7 @@ public NetworkManager(IWebDriver driver) this.session = new Lazy(() => { IDevTools devToolsDriver = driver as IDevTools; - if (session == null) + if (devToolsDriver == null) { throw new WebDriverException("Driver must implement IDevTools to use these features"); } diff --git a/dotnet/src/webdriver/NoAlertPresentException.cs b/dotnet/src/webdriver/NoAlertPresentException.cs index 5573c60308fdb..72c9600280e55 100644 --- a/dotnet/src/webdriver/NoAlertPresentException.cs +++ b/dotnet/src/webdriver/NoAlertPresentException.cs @@ -57,17 +57,5 @@ public NoAlertPresentException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected NoAlertPresentException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/NoSuchDriverException.cs b/dotnet/src/webdriver/NoSuchDriverException.cs index c665131741982..9bd0bf09be271 100644 --- a/dotnet/src/webdriver/NoSuchDriverException.cs +++ b/dotnet/src/webdriver/NoSuchDriverException.cs @@ -64,18 +64,6 @@ public NoSuchDriverException(string message, Exception innerException) { } - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected NoSuchDriverException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - /// /// Add information about obtaining additional support from documentation to this exception. /// diff --git a/dotnet/src/webdriver/NoSuchElementException.cs b/dotnet/src/webdriver/NoSuchElementException.cs index ab6150212818b..cfce90ed7e621 100644 --- a/dotnet/src/webdriver/NoSuchElementException.cs +++ b/dotnet/src/webdriver/NoSuchElementException.cs @@ -64,18 +64,6 @@ public NoSuchElementException(string message, Exception innerException) { } - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected NoSuchElementException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - /// /// Add information about obtaining additional support from documentation to this exception. /// diff --git a/dotnet/src/webdriver/NoSuchFrameException.cs b/dotnet/src/webdriver/NoSuchFrameException.cs index 6cd8d57d0894d..a3c98cef0509e 100644 --- a/dotnet/src/webdriver/NoSuchFrameException.cs +++ b/dotnet/src/webdriver/NoSuchFrameException.cs @@ -57,17 +57,5 @@ public NoSuchFrameException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected NoSuchFrameException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/NoSuchShadowRootException.cs b/dotnet/src/webdriver/NoSuchShadowRootException.cs index e008da17ec11b..32398d6aecfd2 100644 --- a/dotnet/src/webdriver/NoSuchShadowRootException.cs +++ b/dotnet/src/webdriver/NoSuchShadowRootException.cs @@ -57,17 +57,5 @@ public NoSuchShadowRootException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected NoSuchShadowRootException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/NoSuchWindowException.cs b/dotnet/src/webdriver/NoSuchWindowException.cs index a9674d45f881a..410d1878e3076 100644 --- a/dotnet/src/webdriver/NoSuchWindowException.cs +++ b/dotnet/src/webdriver/NoSuchWindowException.cs @@ -57,17 +57,5 @@ public NoSuchWindowException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected NoSuchWindowException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/NotFoundException.cs b/dotnet/src/webdriver/NotFoundException.cs index 1316d83f7c2c1..1621ca93d999e 100644 --- a/dotnet/src/webdriver/NotFoundException.cs +++ b/dotnet/src/webdriver/NotFoundException.cs @@ -57,17 +57,5 @@ public NotFoundException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected NotFoundException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/PrintOptions.cs b/dotnet/src/webdriver/PrintOptions.cs index 2c4c82a12bef3..b5c4959a8270a 100644 --- a/dotnet/src/webdriver/PrintOptions.cs +++ b/dotnet/src/webdriver/PrintOptions.cs @@ -101,19 +101,27 @@ public bool ShrinkToFit } /// - /// Gets the dimensions for each page in the printed document. + /// Gets or sets the dimensions for each page in the printed document. /// public PageSize PageDimensions { get { return pageSize; } + set + { + pageSize = value ?? throw new ArgumentNullException(nameof(value)); + } } /// - /// Gets the margins for each page in the doucment. + /// Gets or sets the margins for each page in the doucment. /// public Margins PageMargins { get { return margins; } + set + { + margins = value ?? throw new ArgumentNullException(nameof(value)); + } } /// diff --git a/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs b/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs index f529b2aa0dbf1..251c6bca78fe1 100644 --- a/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs +++ b/dotnet/src/webdriver/Remote/HttpCommandExecutor.cs @@ -21,6 +21,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; @@ -417,7 +418,11 @@ protected override async Task SendAsync(HttpRequestMessage var responseTask = base.SendAsync(request, cancellationToken); StringBuilder requestLogMessageBuilder = new(); - requestLogMessageBuilder.AppendFormat(">> {0}", request); + requestLogMessageBuilder.AppendFormat(">> {0} RequestUri: {1}, Content: {2}, Headers: {3}", + request.Method, + request.RequestUri?.ToString() ?? "null", + request.Content?.ToString() ?? "null", + request.Headers?.Count()); if (request.Content != null) { @@ -430,7 +435,7 @@ protected override async Task SendAsync(HttpRequestMessage var response = await responseTask.ConfigureAwait(false); StringBuilder responseLogMessageBuilder = new(); - responseLogMessageBuilder.AppendFormat("<< {0}", response); + responseLogMessageBuilder.AppendFormat("<< StatusCode: {0}, ReasonPhrase: {1}, Content: {2}, Headers: {3}", (int)response.StatusCode, response.ReasonPhrase, response.Content, response.Headers?.Count()); if (!response.IsSuccessStatusCode && response.Content != null) { diff --git a/dotnet/src/webdriver/Remote/W3CWireProtocolCommandInfoRepository.cs b/dotnet/src/webdriver/Remote/W3CWireProtocolCommandInfoRepository.cs index 384599ac6ef92..162add5ea0f45 100644 --- a/dotnet/src/webdriver/Remote/W3CWireProtocolCommandInfoRepository.cs +++ b/dotnet/src/webdriver/Remote/W3CWireProtocolCommandInfoRepository.cs @@ -133,6 +133,8 @@ protected override void InitializeCommandDictionary() // local-end implementation of WebDriver. this.TryAddCommand(DriverCommand.IsElementDisplayed, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/displayed")); this.TryAddCommand(DriverCommand.ElementEquals, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/equals/{other}")); + this.TryAddCommand(DriverCommand.GetLog, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/log")); + this.TryAddCommand(DriverCommand.GetAvailableLogTypes, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/se/log/types")); this.TryAddCommand(DriverCommand.UploadFile, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/file")); this.TryAddCommand(DriverCommand.GetDownloadableFiles, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/se/files")); this.TryAddCommand(DriverCommand.DownloadFile, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/se/files")); 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/Safari/SafariDriverService.cs b/dotnet/src/webdriver/Safari/SafariDriverService.cs index 5d850c7ee3bcb..b496252ee3d14 100644 --- a/dotnet/src/webdriver/Safari/SafariDriverService.cs +++ b/dotnet/src/webdriver/Safari/SafariDriverService.cs @@ -17,7 +17,6 @@ // using OpenQA.Selenium.Internal; -using OpenQA.Selenium.Internal.Logging; using System; using System.IO; using System.Net; @@ -34,8 +33,6 @@ public sealed class SafariDriverService : DriverService { private const string DefaultSafariDriverServiceExecutableName = "safaridriver"; - private readonly static ILogger logger = Log.GetLogger(); - /// /// Initializes a new instance of the class. /// @@ -130,10 +127,7 @@ protected override bool IsInitialized // check. catch (Exception ex) when (ex is HttpRequestException || ex is TaskCanceledException) { - if (logger.IsEnabled(LogEventLevel.Trace)) - { - logger.Trace(ex.ToString()); - } + // Do nothing. The exception is expected, meaning driver service is not initialized. } } } diff --git a/dotnet/src/webdriver/SeleniumManager.cs b/dotnet/src/webdriver/SeleniumManager.cs index 5e365ede075b6..60c8cd5255389 100644 --- a/dotnet/src/webdriver/SeleniumManager.cs +++ b/dotnet/src/webdriver/SeleniumManager.cs @@ -24,7 +24,10 @@ using System.Runtime.InteropServices; using System.Text; using System.Text.Json; -using System.Text.Json.Nodes; +using System.Text.Json.Serialization; +using static OpenQA.Selenium.SeleniumManagerResponse; + +#nullable enable namespace OpenQA.Selenium { @@ -36,25 +39,25 @@ public static class SeleniumManager { private static readonly ILogger _logger = Log.GetLogger(typeof(SeleniumManager)); - private static readonly string BinaryFullPath = Environment.GetEnvironmentVariable("SE_MANAGER_PATH"); + private static readonly JsonSerializerOptions _serializerOptions = new() { PropertyNameCaseInsensitive = true, TypeInfoResolver = SeleniumManagerSerializerContext.Default }; - static SeleniumManager() + private static readonly Lazy _lazyBinaryFullPath = new(() => { - - if (BinaryFullPath == null) + string? binaryFullPath = Environment.GetEnvironmentVariable("SE_MANAGER_PATH"); + if (binaryFullPath == null) { var currentDirectory = AppContext.BaseDirectory; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { - BinaryFullPath = Path.Combine(currentDirectory, "selenium-manager", "windows", "selenium-manager.exe"); + binaryFullPath = Path.Combine(currentDirectory, "selenium-manager", "windows", "selenium-manager.exe"); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { - BinaryFullPath = Path.Combine(currentDirectory, "selenium-manager", "linux", "selenium-manager"); + binaryFullPath = Path.Combine(currentDirectory, "selenium-manager", "linux", "selenium-manager"); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) { - BinaryFullPath = Path.Combine(currentDirectory, "selenium-manager", "macos", "selenium-manager"); + binaryFullPath = Path.Combine(currentDirectory, "selenium-manager", "macos", "selenium-manager"); } else { @@ -63,11 +66,13 @@ static SeleniumManager() } } - if (!File.Exists(BinaryFullPath)) + if (!File.Exists(binaryFullPath)) { - throw new WebDriverException($"Unable to locate or obtain Selenium Manager binary at {BinaryFullPath}"); + throw new WebDriverException($"Unable to locate or obtain Selenium Manager binary at {binaryFullPath}"); } - } + + return binaryFullPath; + }); /// /// Determines the location of the browser and driver binaries. @@ -86,10 +91,12 @@ public static Dictionary BinaryPaths(string arguments) argsBuilder.Append(" --debug"); } - var output = RunCommand(BinaryFullPath, argsBuilder.ToString()); - Dictionary binaryPaths = new Dictionary(); - binaryPaths.Add("browser_path", (string)output["browser_path"]); - binaryPaths.Add("driver_path", (string)output["driver_path"]); + var smCommandResult = RunCommand(_lazyBinaryFullPath.Value, argsBuilder.ToString()); + Dictionary binaryPaths = new() + { + { "browser_path", smCommandResult.BrowserPath }, + { "driver_path", smCommandResult.DriverPath } + }; if (_logger.IsEnabled(LogEventLevel.Trace)) { @@ -108,10 +115,10 @@ public static Dictionary BinaryPaths(string arguments) /// /// the standard output of the execution. /// - private static JsonNode RunCommand(string fileName, string arguments) + private static ResultResponse RunCommand(string fileName, string arguments) { Process process = new Process(); - process.StartInfo.FileName = BinaryFullPath; + process.StartInfo.FileName = _lazyBinaryFullPath.Value; process.StartInfo.Arguments = arguments; process.StartInfo.UseShellExecute = false; process.StartInfo.CreateNoWindow = true; @@ -174,47 +181,47 @@ private static JsonNode RunCommand(string fileName, string arguments) } string output = outputBuilder.ToString().Trim(); - JsonNode resultJsonNode; + + SeleniumManagerResponse jsonResponse; + try { - var deserializedOutput = JsonSerializer.Deserialize>(output); - resultJsonNode = deserializedOutput["result"]; + jsonResponse = JsonSerializer.Deserialize(output, _serializerOptions)!; } catch (Exception ex) { throw new WebDriverException($"Error deserializing Selenium Manager's response: {output}", ex); } - if (resultJsonNode["logs"] is not null) + if (jsonResponse.Logs is not null) { - var logs = resultJsonNode["logs"]; - foreach (var entry in logs.AsArray()) + // Treat SM's logs always as Trace to avoid SM writing at Info level + if (_logger.IsEnabled(LogEventLevel.Trace)) { - switch (entry.GetPropertyName()) + foreach (var entry in jsonResponse.Logs) { - case "WARN": - if (_logger.IsEnabled(LogEventLevel.Warn)) - { - _logger.Warn(entry.GetValue()); - } - break; - case "DEBUG": - if (_logger.IsEnabled(LogEventLevel.Debug)) - { - _logger.Debug(entry.GetValue()); - } - break; - case "INFO": - if (_logger.IsEnabled(LogEventLevel.Info)) - { - _logger.Info(entry.GetValue()); - } - break; + _logger.Trace($"{entry.Level} {entry.Message}"); } } } - return resultJsonNode; + return jsonResponse.Result; } } + + internal record SeleniumManagerResponse(IReadOnlyList Logs, ResultResponse Result) + { + public record LogEntryResponse(string Level, string Message); + + public record ResultResponse + ( + [property: JsonPropertyName("driver_path")] + string DriverPath, + [property: JsonPropertyName("browser_path")] + string BrowserPath + ); + } + + [JsonSerializable(typeof(SeleniumManagerResponse))] + internal partial class SeleniumManagerSerializerContext : JsonSerializerContext; } diff --git a/dotnet/src/webdriver/StaleElementReferenceException.cs b/dotnet/src/webdriver/StaleElementReferenceException.cs index a7c7f68de56f4..81f1fdcd97948 100644 --- a/dotnet/src/webdriver/StaleElementReferenceException.cs +++ b/dotnet/src/webdriver/StaleElementReferenceException.cs @@ -63,18 +63,6 @@ public StaleElementReferenceException(string message, Exception innerException) { } - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected StaleElementReferenceException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - /// /// Add information about obtaining additional support from documentation to this exception. /// diff --git a/dotnet/src/webdriver/UnableToSetCookieException.cs b/dotnet/src/webdriver/UnableToSetCookieException.cs index 40e873d714bd0..6ff0d1ddcf5e8 100644 --- a/dotnet/src/webdriver/UnableToSetCookieException.cs +++ b/dotnet/src/webdriver/UnableToSetCookieException.cs @@ -57,17 +57,5 @@ public UnableToSetCookieException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected UnableToSetCookieException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/UnhandledAlertException.cs b/dotnet/src/webdriver/UnhandledAlertException.cs index fe2b56c6b3eb0..7126a7326728e 100644 --- a/dotnet/src/webdriver/UnhandledAlertException.cs +++ b/dotnet/src/webdriver/UnhandledAlertException.cs @@ -72,18 +72,6 @@ public UnhandledAlertException(string message, Exception innerException) { } - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected UnhandledAlertException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } - /// /// Gets the text of the unhandled alert. /// @@ -91,17 +79,5 @@ public string AlertText { get { return this.alertText; } } - - /// - /// Populates a SerializationInfo with the data needed to serialize the target object. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - base.GetObjectData(info, context); - } } } diff --git a/dotnet/src/webdriver/WebDriver.csproj b/dotnet/src/webdriver/WebDriver.csproj index 2b0c63d660eea..e9acf442d70c1 100644 --- a/dotnet/src/webdriver/WebDriver.csproj +++ b/dotnet/src/webdriver/WebDriver.csproj @@ -71,7 +71,7 @@ - + ..\..\..\bazel-bin\dotnet\src\webdriver\manager @@ -79,7 +79,7 @@ - + @@ -106,7 +106,7 @@ - + diff --git a/dotnet/src/webdriver/WebDriverArgumentException.cs b/dotnet/src/webdriver/WebDriverArgumentException.cs index fd92a38fd8091..dcb16ca2cf38c 100644 --- a/dotnet/src/webdriver/WebDriverArgumentException.cs +++ b/dotnet/src/webdriver/WebDriverArgumentException.cs @@ -57,17 +57,5 @@ public WebDriverArgumentException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected WebDriverArgumentException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/WebDriverException.cs b/dotnet/src/webdriver/WebDriverException.cs index 9eb7af0816241..787f5feed25c4 100644 --- a/dotnet/src/webdriver/WebDriverException.cs +++ b/dotnet/src/webdriver/WebDriverException.cs @@ -67,17 +67,5 @@ public WebDriverException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected WebDriverException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/WebDriverTimeoutException.cs b/dotnet/src/webdriver/WebDriverTimeoutException.cs index caedb65302a33..eecadbb915523 100644 --- a/dotnet/src/webdriver/WebDriverTimeoutException.cs +++ b/dotnet/src/webdriver/WebDriverTimeoutException.cs @@ -57,17 +57,5 @@ public WebDriverTimeoutException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected WebDriverTimeoutException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/XPathLookupException.cs b/dotnet/src/webdriver/XPathLookupException.cs index 81d47ac896e3f..eca80a9e29aba 100644 --- a/dotnet/src/webdriver/XPathLookupException.cs +++ b/dotnet/src/webdriver/XPathLookupException.cs @@ -57,17 +57,5 @@ public XPathLookupException(string message, Exception innerException) : base(message, innerException) { } - - /// - /// Initializes a new instance of the class with serialized data. - /// - /// The that holds the serialized - /// object data about the exception being thrown. - /// The that contains contextual - /// information about the source or destination. - protected XPathLookupException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - } } } diff --git a/dotnet/src/webdriver/cdp/README.md b/dotnet/src/webdriver/cdp/README.md index 04fca71638762..f78d0b3d04ba4 100644 --- a/dotnet/src/webdriver/cdp/README.md +++ b/dotnet/src/webdriver/cdp/README.md @@ -52,9 +52,4 @@ perform the following steps, where `` is the major version of the protocol: remove the entry for version `` from the `SupportedDevToolsVersions` dictionary initialization. 3. Remove the version string (`v`) from the `SUPPORTED_DEVTOOLS_VERSIONS` list in [`//dotnet:selenium-dotnet-version.bzl`](https://github.com/SeleniumHQ/selenium/blob/trunk/dotnet/selenium-dotnet-version.bzl). -4. In [`//dotnet/src/webdriver:WebDriver.csproj.prebuild.cmd`](https://github.com/SeleniumHQ/selenium/blob/trunk/dotnet/src/webdriver/WebDriver.csproj.prebuild.cmd), -remove the `if not exist` block for version ``. -5. In [`//dotnet/src/webdriver:WebDriver.csproj.prebuild.sh`](https://github.com/SeleniumHQ/selenium/blob/trunk/dotnet/src/webdriver/WebDriver.csproj.prebuild.sh), -remove the `if-fi` block for version ``. -6. Commit the changes. - +4. Commit the changes. 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 98d7291b725f4..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 @@ -203,7 +203,6 @@ await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings } [Test] - [IgnorePlatform("Windows", "Not working properly")] [IgnoreBrowser(Selenium.Browser.IE, "IE does not support Chrome DevTools Protocol")] [IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Chrome DevTools Protocol")] [IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Chrome DevTools Protocol")] @@ -232,7 +231,8 @@ await domains.Network.Enable(new CurrentCdpVersion.Network.EnableCommandSettings var searchResponse = await domains.Network.SearchInResponseBody(new CurrentCdpVersion.Network.SearchInResponseBodyCommandSettings() { RequestId = requestIds[0], - Query = "/", + Query = ".*", + IsRegex = true }); Assert.That(searchResponse.Result.Length > 0); @@ -386,9 +386,10 @@ public async Task VerifyRequestPostData() if (string.Compare(e.Request.Method, "post", StringComparison.OrdinalIgnoreCase) == 0) { requestIds[0] = e.RequestId; + requestSync.Set(); } - requestSync.Set(); }; + domains.Network.RequestWillBeSent += requestWillBeSentHandler; driver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("postForm.html"); 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/DriverTestFixture.cs b/dotnet/test/common/DriverTestFixture.cs index fd5cc5e693ae1..9fa1757e9b011 100644 --- a/dotnet/test/common/DriverTestFixture.cs +++ b/dotnet/test/common/DriverTestFixture.cs @@ -88,6 +88,8 @@ public abstract class DriverTestFixture public string scrollFrameOutOfViewport = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/frame_with_nested_scrolling_frame_out_of_view.html"); public string scrollFrameInViewport = EnvironmentManager.Instance.UrlBuilder.WhereIs("scrolling_tests/frame_with_nested_scrolling_frame.html"); + public string printPage = EnvironmentManager.Instance.UrlBuilder.WhereIs("printPage.html"); + protected IWebDriver driver; public IWebDriver DriverInstance 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/dotnet/test/common/JavascriptEnabledBrowserTest.cs b/dotnet/test/common/JavascriptEnabledBrowserTest.cs index 390f3120ae044..78c5a8515c3d1 100644 --- a/dotnet/test/common/JavascriptEnabledBrowserTest.cs +++ b/dotnet/test/common/JavascriptEnabledBrowserTest.cs @@ -34,6 +34,7 @@ public void DocumentShouldReflectLatestDom() [Test] [IgnoreBrowser(Browser.Chrome, "Not working properly in Chrome")] + [IgnoreBrowser(Browser.Edge, "Not working properly in Edge")] public void ShouldWaitForLoadsToCompleteAfterJavascriptCausesANewPageToLoad() { driver.Url = formsPage; @@ -45,6 +46,7 @@ public void ShouldWaitForLoadsToCompleteAfterJavascriptCausesANewPageToLoad() [Test] [IgnoreBrowser(Browser.Chrome, "Not working properly in Chrome")] + [IgnoreBrowser(Browser.Edge, "Not working properly in Edge")] public void ShouldBeAbleToFindElementAfterJavascriptCausesANewPageToLoad() { driver.Url = formsPage; diff --git a/dotnet/test/common/PrintTest.cs b/dotnet/test/common/PrintTest.cs new file mode 100644 index 0000000000000..1c31882d2db8b --- /dev/null +++ b/dotnet/test/common/PrintTest.cs @@ -0,0 +1,74 @@ +using NUnit.Framework; +using System; + +namespace OpenQA.Selenium +{ + [TestFixture] + public class PrintTest : DriverTestFixture + { + private const string MagicString = "JVBER"; + private ISupportsPrint printer; + + [SetUp] + public void LocalSetUp() + { + Assert.That(driver, Is.InstanceOf(), $"Driver does not support {nameof(ISupportsPrint)}."); + + printer = driver as ISupportsPrint; + + driver.Navigate().GoToUrl(this.printPage); + } + + [Test] + public void CanPrintPage() + { + var pdf = printer.Print(new PrintOptions()); + + Assert.That(pdf.AsBase64EncodedString, Does.Contain(MagicString)); + } + + [Test] + public void CanPrintTwoPages() + { + var options = new PrintOptions(); + + options.AddPageRangeToPrint("1-2"); + + var pdf = printer.Print(options); + + Assert.That(pdf.AsBase64EncodedString, Does.Contain(MagicString)); + } + + [Test] + public void CanPrintWithMostParams() + { + var options = new PrintOptions() + { + Orientation = PrintOrientation.Landscape, + ScaleFactor = 0.5, + PageDimensions = new PrintOptions.PageSize { Width = 200, Height = 100 }, + PageMargins = new PrintOptions.Margins { Top = 1, Bottom = 1, Left = 2, Right = 2 }, + OutputBackgroundImages = true, + ShrinkToFit = false + }; + + options.AddPageRangeToPrint("1-3"); + + var pdf = printer.Print(options); + + Assert.That(pdf.AsBase64EncodedString, Does.Contain(MagicString)); + } + + [Test] + public void PageSizeCannotBeNull() + { + Assert.That(() => new PrintOptions { PageDimensions = null }, Throws.InstanceOf()); + } + + [Test] + public void MarginsCannotBeNull() + { + Assert.That(() => new PrintOptions { PageMargins = null }, Throws.InstanceOf()); + } + } +} diff --git a/dotnet/test/common/WebDriver.Common.Tests.csproj b/dotnet/test/common/WebDriver.Common.Tests.csproj index 720d0f36162d2..60f81c5351152 100644 --- a/dotnet/test/common/WebDriver.Common.Tests.csproj +++ b/dotnet/test/common/WebDriver.Common.Tests.csproj @@ -34,7 +34,7 @@ - + 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 34adb920c307a..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": -809854593, - "__RESOLVED_ARTIFACTS_HASH": -1445024694, + "__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.0-jre", + "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": { @@ -159,10 +159,10 @@ }, "com.google.guava:guava": { "shasums": { - "jar": "dfadc3bce3101eff1452aae47d7c833fee443b47bdf9ef13311b6c7cab663ddf", - "sources": "f91f8619f533db55f37d13369c2fee39d5e1d2f72cef7f69f735d5be1a601f14" + "jar": "4bf0e2c5af8e4525c96e8fde17a4f7307f97f8478f11c4c8e35a0e3298ae4e90", + "sources": "b7cbdad958b791f2a036abff7724570bf9836531c460966f8a3d0df8eaa1c21d" }, - "version": "33.3.0-jre" + "version": "33.3.1-jre" }, "com.google.guava:guava-testlib": { "shasums": { @@ -194,17 +194,17 @@ }, "com.graphql-java:graphql-java": { "shasums": { - "jar": "98c63c1bf51876f84a3770573279be4f98bbfc2c86d6b4819c327fa1cbd2b137", - "sources": "2f8be51261734b4618551724a5730d1e6299a9adda171178027eacdc8010363a" + "jar": "8828fef5d8133d3d5ad23cee262a9b3ab4ce95aedf5e3332bb577a9aa7c627e0", + "sources": "122d4adc1c1491f86f08f6ba6206aa9f05d14a02cf36c73baad7e1d8034160a7" }, - "version": "20.2" + "version": "22.3" }, "com.graphql-java:java-dataloader": { "shasums": { - "jar": "b9c7d32aef05a2e33dc07c5ce45b713c405b61c6264cb0ed48aac003add3eaa4", - "sources": "ffecf802d587b05860d8ab116d3c6a8630f8eca5d3b5da783f8dd085e2ca0591" + "jar": "08cec84ac76e32b53ea666260f288f10b3731c21c89f9199b109ced2361f78b8", + "sources": "cea71f74025c2ca95618113345f888c780a99bf3133621b54007e1babaef2e14" }, - "version": "3.2.0" + "version": "3.3.0" }, "commons-codec:commons-codec": { "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": { @@ -900,8 +900,7 @@ ], "com.graphql-java:graphql-java": [ "com.graphql-java:java-dataloader", - "org.reactivestreams:reactive-streams", - "org.slf4j:slf4j-api" + "org.reactivestreams:reactive-streams" ], "com.graphql-java:java-dataloader": [ "org.slf4j:slf4j-api" @@ -1209,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", @@ -1425,16 +1424,18 @@ ], "com.graphql-java:graphql-java": [ "graphql", + "graphql.agent.result", "graphql.analysis", "graphql.analysis.values", - "graphql.cachecontrol", "graphql.collect", "graphql.com.google.common.base", "graphql.com.google.common.collect", "graphql.com.google.common.math", "graphql.com.google.common.primitives", "graphql.execution", + "graphql.execution.conditional", "graphql.execution.directives", + "graphql.execution.incremental", "graphql.execution.instrumentation", "graphql.execution.instrumentation.dataloader", "graphql.execution.instrumentation.fieldvalidation", @@ -1444,11 +1445,15 @@ "graphql.execution.preparsed", "graphql.execution.preparsed.persisted", "graphql.execution.reactive", + "graphql.execution.values", + "graphql.execution.values.legacycoercing", "graphql.extensions", "graphql.i18n", + "graphql.incremental", "graphql.introspection", "graphql.language", "graphql.normalized", + "graphql.normalized.incremental", "graphql.org.antlr.v4.runtime", "graphql.org.antlr.v4.runtime.atn", "graphql.org.antlr.v4.runtime.dfa", @@ -1466,7 +1471,6 @@ "graphql.schema.diff.reporting", "graphql.schema.diffing", "graphql.schema.diffing.ana", - "graphql.schema.diffing.dot", "graphql.schema.fetching", "graphql.schema.idl", "graphql.schema.idl.errors", @@ -1475,6 +1479,7 @@ "graphql.schema.usage", "graphql.schema.validation", "graphql.schema.visibility", + "graphql.schema.visitor", "graphql.util", "graphql.validation", "graphql.validation.rules" @@ -1484,6 +1489,7 @@ "org.dataloader.annotations", "org.dataloader.impl", "org.dataloader.registries", + "org.dataloader.scheduler", "org.dataloader.stats", "org.dataloader.stats.context" ], @@ -2608,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/spotbugs-excludes.xml b/java/spotbugs-excludes.xml index bb0918aea3d02..389ebc78a76f3 100644 --- a/java/spotbugs-excludes.xml +++ b/java/spotbugs-excludes.xml @@ -204,4 +204,30 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/src/org/openqa/selenium/chromium/ChromiumDriver.java b/java/src/org/openqa/selenium/chromium/ChromiumDriver.java index 7a87460878313..f5518714d436f 100644 --- a/java/src/org/openqa/selenium/chromium/ChromiumDriver.java +++ b/java/src/org/openqa/selenium/chromium/ChromiumDriver.java @@ -212,7 +212,8 @@ public ScriptKey pin(String script) { // Create the actual script we're going to use. String scriptToUse = String.format( - "window.seleniumPinnedScript%s = function(){%s}", Math.abs(script.hashCode()), script); + "window.seleniumPinnedScript%s = function(){%s}", + Math.abs((long) script.hashCode()), script); DevTools devTools = getDevTools(); devTools.createSessionIfThereIsNotOne(); diff --git a/java/src/org/openqa/selenium/devtools/v128/v128Network.java b/java/src/org/openqa/selenium/devtools/v128/v128Network.java index e333a026dfc64..ccbdcee9426f3 100644 --- a/java/src/org/openqa/selenium/devtools/v128/v128Network.java +++ b/java/src/org/openqa/selenium/devtools/v128/v128Network.java @@ -127,7 +127,7 @@ public Either createSeMessages(RequestPaused pausedRe } catch (DevToolsException e) { // Redirects don't seem to have bodies int code = pausedReq.getResponseStatusCode().orElse(HTTP_OK); - if (code < 300 && code > 399) { + if (code >= 300 && code <= 399) { LOG.warning("Unable to get body for request id " + pausedReq.getRequestId()); } diff --git a/java/src/org/openqa/selenium/devtools/v129/v129Network.java b/java/src/org/openqa/selenium/devtools/v129/v129Network.java index 41c76f9897c46..21651783d74cf 100644 --- a/java/src/org/openqa/selenium/devtools/v129/v129Network.java +++ b/java/src/org/openqa/selenium/devtools/v129/v129Network.java @@ -127,7 +127,7 @@ public Either createSeMessages(RequestPaused pausedRe } catch (DevToolsException e) { // Redirects don't seem to have bodies int code = pausedReq.getResponseStatusCode().orElse(HTTP_OK); - if (code < 300 && code > 399) { + if (code >= 300 && code <= 399) { LOG.warning("Unable to get body for request id " + pausedReq.getRequestId()); } 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 91% rename from java/src/org/openqa/selenium/devtools/v127/v127Network.java rename to java/src/org/openqa/selenium/devtools/v130/v130Network.java index d430a582b2358..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 @@ -127,7 +127,7 @@ public Either createSeMessages(RequestPaused pausedRe } catch (DevToolsException e) { // Redirects don't seem to have bodies int code = pausedReq.getResponseStatusCode().orElse(HTTP_OK); - if (code < 300 && code > 399) { + if (code >= 300 && code <= 399) { LOG.warning("Unable to get body for request id " + pausedReq.getRequestId()); } 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/v85/V85Network.java b/java/src/org/openqa/selenium/devtools/v85/V85Network.java index da12dbe42802d..c4bc0df6219ce 100644 --- a/java/src/org/openqa/selenium/devtools/v85/V85Network.java +++ b/java/src/org/openqa/selenium/devtools/v85/V85Network.java @@ -137,7 +137,7 @@ public Either createSeMessages(RequestPaused pausedRe } catch (DevToolsException e) { // Redirects don't seem to have bodies int code = pausedReq.getResponseStatusCode().orElse(HTTP_OK); - if (code < 300 && code > 399) { + if (code >= 300 && code <= 399) { LOG.warning("Unable to get body for request id " + pausedReq.getRequestId()); } 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/data/NodeStatus.java b/java/src/org/openqa/selenium/grid/data/NodeStatus.java index 47753585ec989..c9ce38614fcea 100644 --- a/java/src/org/openqa/selenium/grid/data/NodeStatus.java +++ b/java/src/org/openqa/selenium/grid/data/NodeStatus.java @@ -214,6 +214,8 @@ public boolean equals(Object o) { return Objects.equals(this.nodeId, that.nodeId) && Objects.equals(this.externalUri, that.externalUri) && this.maxSessionCount == that.maxSessionCount + && this.sessionTimeout.compareTo(that.sessionTimeout) == 0 + && this.heartbeatPeriod.compareTo(that.heartbeatPeriod) == 0 && Objects.equals(this.slots, that.slots) && Objects.equals(this.availability, that.availability) && Objects.equals(this.version, that.version); @@ -224,7 +226,7 @@ public int hashCode() { return Objects.hash(nodeId, externalUri, maxSessionCount, slots, version); } - private Map toJson() { + public Map toJson() { Map toReturn = new TreeMap<>(); toReturn.put("nodeId", nodeId); toReturn.put("externalUri", externalUri); diff --git a/java/src/org/openqa/selenium/grid/graphql/BUILD.bazel b/java/src/org/openqa/selenium/grid/graphql/BUILD.bazel index 7c008441e8699..4fc0d84d5aa70 100644 --- a/java/src/org/openqa/selenium/grid/graphql/BUILD.bazel +++ b/java/src/org/openqa/selenium/grid/graphql/BUILD.bazel @@ -21,6 +21,5 @@ java_library( "//java/src/org/openqa/selenium/remote/http", artifact("com.google.guava:guava"), artifact("com.graphql-java:graphql-java"), - artifact("com.graphql-java:java-dataloader"), ], ) diff --git a/java/src/org/openqa/selenium/grid/graphql/GraphqlHandler.java b/java/src/org/openqa/selenium/grid/graphql/GraphqlHandler.java index 28f35c477cbbe..3ab7a7385f701 100644 --- a/java/src/org/openqa/selenium/grid/graphql/GraphqlHandler.java +++ b/java/src/org/openqa/selenium/grid/graphql/GraphqlHandler.java @@ -45,6 +45,7 @@ import java.net.URI; import java.util.HashMap; import java.util.Map; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import org.openqa.selenium.grid.distributor.Distributor; import org.openqa.selenium.grid.sessionqueue.NewSessionQueue; @@ -88,7 +89,7 @@ public GraphqlHandler( new SchemaGenerator() .makeExecutableSchema(buildTypeDefinitionRegistry(), buildRuntimeWiring()); - Cache cache = + Cache> cache = CacheBuilder.newBuilder().maximumSize(1024).build(); graphQl = @@ -97,7 +98,10 @@ public GraphqlHandler( (executionInput, computeFunction) -> { try { return cache.get( - executionInput.getQuery(), () -> computeFunction.apply(executionInput)); + executionInput.getQuery(), + () -> + CompletableFuture.supplyAsync( + () -> computeFunction.apply(executionInput))); } catch (ExecutionException e) { if (e.getCause() instanceof RuntimeException) { throw (RuntimeException) e.getCause(); diff --git a/java/src/org/openqa/selenium/grid/graphql/Grid.java b/java/src/org/openqa/selenium/grid/graphql/Grid.java index da67f9f6e586f..a6eb03cb6e35d 100644 --- a/java/src/org/openqa/selenium/grid/graphql/Grid.java +++ b/java/src/org/openqa/selenium/grid/graphql/Grid.java @@ -97,6 +97,7 @@ public List getNodes() { status.getExternalUri(), status.getAvailability(), status.getMaxSessionCount(), + status.getSessionTimeout(), status.getSlots().size(), stereotypes, sessions, diff --git a/java/src/org/openqa/selenium/grid/graphql/Node.java b/java/src/org/openqa/selenium/grid/graphql/Node.java index 564ef6e8eef95..267789c5a8fd3 100644 --- a/java/src/org/openqa/selenium/grid/graphql/Node.java +++ b/java/src/org/openqa/selenium/grid/graphql/Node.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableList; import java.net.URI; +import java.time.Duration; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -38,6 +39,7 @@ public class Node { private final URI uri; private final Availability status; private final int maxSession; + private final Duration sessionTimeout; private final Map stereotypes; private final Map activeSessions; private final String version; @@ -49,6 +51,7 @@ public Node( URI uri, Availability status, int maxSession, + Duration sessionTimeout, int slotCount, Map stereotypes, Map activeSessions, @@ -63,6 +66,7 @@ public Node( this.activeSessions = Require.nonNull("Active sessions", activeSessions); this.version = Require.nonNull("Grid Node version", version); this.osInfo = Require.nonNull("Grid Node OS info", osInfo); + this.sessionTimeout = Require.positive("Node session timeout", sessionTimeout); } public List getSessions() { @@ -122,6 +126,10 @@ public OsInfo getOsInfo() { return osInfo; } + public Duration getSessionTimeout() { + return sessionTimeout; + } + private org.openqa.selenium.grid.graphql.Session createGraphqlSession( Map.Entry entry) { Session session = entry.getKey(); diff --git a/java/src/org/openqa/selenium/grid/graphql/selenium-grid-schema.graphqls b/java/src/org/openqa/selenium/grid/graphql/selenium-grid-schema.graphqls index 96c5b9e29f6cc..9278611699c88 100644 --- a/java/src/org/openqa/selenium/grid/graphql/selenium-grid-schema.graphqls +++ b/java/src/org/openqa/selenium/grid/graphql/selenium-grid-schema.graphqls @@ -38,6 +38,7 @@ type Node { uri: Uri! status: Status! maxSession: Int! + sessionTimeout: String! slotCount: Int! sessions: [Session]! sessionCount: Int! diff --git a/java/src/org/openqa/selenium/grid/node/ForwardWebDriverCommand.java b/java/src/org/openqa/selenium/grid/node/ForwardWebDriverCommand.java index 458dfff372cbe..fadcfff4c80fd 100644 --- a/java/src/org/openqa/selenium/grid/node/ForwardWebDriverCommand.java +++ b/java/src/org/openqa/selenium/grid/node/ForwardWebDriverCommand.java @@ -17,7 +17,13 @@ package org.openqa.selenium.grid.node; +import static java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; +import static org.openqa.selenium.remote.HttpSessionId.getSessionId; +import static org.openqa.selenium.remote.http.Contents.asJson; + +import com.google.common.collect.ImmutableMap; 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; @@ -30,8 +36,22 @@ class ForwardWebDriverCommand implements HttpHandler { this.node = Require.nonNull("Node", node); } + public boolean matches(HttpRequest req) { + return getSessionId(req.getUri()) + .map(id -> node.isSessionOwner(new SessionId(id))) + .orElse(false); + } + @Override public HttpResponse execute(HttpRequest req) { - return node.executeWebDriverCommand(req); + if (matches(req)) { + return node.executeWebDriverCommand(req); + } + return new HttpResponse() + .setStatus(HTTP_INTERNAL_ERROR) + .setContent( + asJson( + ImmutableMap.of( + "error", String.format("Session not found in node %s", node.getId())))); } } diff --git a/java/src/org/openqa/selenium/grid/node/Node.java b/java/src/org/openqa/selenium/grid/node/Node.java index 5d54bb3e2981d..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 @@ -152,7 +158,7 @@ protected Node( req -> getSessionId(req.getUri()) .map(SessionId::new) - .map(this::isSessionOwner) + .map(sessionId -> this.getSession(sessionId) != null) .orElse(false)) .to(() -> new ForwardWebDriverCommand(this)) .with(spanDecorator("node.forward_command")), @@ -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 3d49df5f713b3..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; @@ -30,7 +31,7 @@ import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_REGISTER_PERIOD; import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_SESSION_TIMEOUT; import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_USE_SELENIUM_MANAGER; -import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_VNC_ENV_VAR; +import static org.openqa.selenium.grid.node.config.NodeOptions.DEFAULT_VNC_ENV_VARS; import static org.openqa.selenium.grid.node.config.NodeOptions.NODE_SECTION; import static org.openqa.selenium.grid.node.config.NodeOptions.OVERRIDE_MAX_SESSIONS; @@ -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, @@ -202,8 +211,11 @@ public class NodeFlags implements HasRoles { description = "Environment variable to check in order to determine if a vnc stream is " + "available or not.") - @ConfigValue(section = NODE_SECTION, name = "vnc-env-var", example = "SE_START_XVFB") - public String vncEnvVar = DEFAULT_VNC_ENV_VAR; + @ConfigValue( + section = NODE_SECTION, + name = "vnc-env-var", + example = "[\"SE_START_XVFB\", \"SE_START_VNC\", \"SE_START_NO_VNC\"]") + public List vncEnvVar = DEFAULT_VNC_ENV_VARS; @Parameter( names = "--no-vnc-port", 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 8f7ea2dd68aaa..ff8fc6d76667a 100644 --- a/java/src/org/openqa/selenium/grid/node/config/NodeOptions.java +++ b/java/src/org/openqa/selenium/grid/node/config/NodeOptions.java @@ -31,6 +31,7 @@ import java.net.URISyntaxException; import java.time.Duration; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; @@ -72,13 +73,15 @@ 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"; static final boolean DEFAULT_DETECT_DRIVERS = true; static final boolean DEFAULT_USE_SELENIUM_MANAGER = false; static final boolean OVERRIDE_MAX_SESSIONS = false; - static final String DEFAULT_VNC_ENV_VAR = "SE_START_XVFB"; + static final List DEFAULT_VNC_ENV_VARS = + Arrays.asList("SE_START_XVFB", "SE_START_VNC", "SE_START_NO_VNC"); static final int DEFAULT_NO_VNC_PORT = 7900; static final int DEFAULT_REGISTER_CYCLE = 10; static final int DEFAULT_REGISTER_PERIOD = 120; @@ -260,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 = @@ -286,9 +298,16 @@ public int getDrainAfterSessionCount() { @VisibleForTesting boolean isVncEnabled() { - String vncEnvVar = config.get(NODE_SECTION, "vnc-env-var").orElse(DEFAULT_VNC_ENV_VAR); + List vncEnvVars = DEFAULT_VNC_ENV_VARS; + if (config.getAll(NODE_SECTION, "vnc-env-var").isPresent()) { + vncEnvVars = config.getAll(NODE_SECTION, "vnc-env-var").get(); + } if (!vncEnabledValueSet.getAndSet(true)) { - vncEnabled.set(Boolean.parseBoolean(System.getenv(vncEnvVar))); + boolean allEnabled = + vncEnvVars.stream() + .allMatch( + env -> "true".equalsIgnoreCase(System.getProperty(env, System.getenv(env)))); + vncEnabled.set(allEnabled); } return vncEnabled.get(); } 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 2283e8573042d..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 @@ -297,7 +301,13 @@ protected LocalNode( heartbeatPeriod.getSeconds(), TimeUnit.SECONDS); - Runtime.getRuntime().addShutdownHook(new Thread(this::stopAllSessions)); + Runtime.getRuntime() + .addShutdownHook( + new Thread( + () -> { + stopAllSessions(); + drain(); + })); new JMXHelper().register(this); } @@ -316,7 +326,6 @@ private void stopTimedOutSession(RemovalNotification not } // Attempt to stop the session slot.stop(); - this.sessionToDownloadsDir.invalidate(id); // Decrement pending sessions if Node is draining if (this.isDraining()) { int done = pendingSessions.decrementAndGet(); @@ -473,8 +482,6 @@ public Either newSession( sessionToDownloadsDir.put(session.getId(), uuidForSessionDownloads); currentSessions.put(session.getId(), slotToUse); - checkSessionCount(); - SessionId sessionId = session.getId(); Capabilities caps = session.getCapabilities(); SESSION_ID.accept(span, sessionId); @@ -513,6 +520,8 @@ public Either newSession( span.addEvent("Unable to create session with the driver", attributeMap); return Either.left(possibleSession.left()); } + } finally { + checkSessionCount(); } } @@ -574,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); @@ -765,6 +792,10 @@ public HttpResponse uploadFile(HttpRequest req, SessionId id) { public void stop(SessionId id) throws NoSuchSessionException { Require.nonNull("Session ID", id); + if (sessionToDownloadsDir.getIfPresent(id) != null) { + sessionToDownloadsDir.invalidate(id); + } + SessionSlot slot = currentSessions.getIfPresent(id); if (slot == null) { throw new NoSuchSessionException("Cannot find session with id: " + id); @@ -978,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); @@ -1032,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, @@ -1048,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/grid/router/GridStatusHandler.java b/java/src/org/openqa/selenium/grid/router/GridStatusHandler.java index c873ecaeb003d..b1fb021cf59a9 100644 --- a/java/src/org/openqa/selenium/grid/router/GridStatusHandler.java +++ b/java/src/org/openqa/selenium/grid/router/GridStatusHandler.java @@ -141,6 +141,7 @@ public HttpResponse execute(HttpRequest req) { .put("id", node.getNodeId()) .put("uri", node.getExternalUri()) .put("maxSessions", node.getMaxSessionCount()) + .put("sessionTimeout", node.getSessionTimeout().toMillis()) .put("osInfo", node.getOsInfo()) .put("heartbeatPeriod", node.getHeartbeatPeriod().toMillis()) .put("availability", node.getAvailability()) diff --git a/java/src/org/openqa/selenium/net/PortProber.java b/java/src/org/openqa/selenium/net/PortProber.java index 7106007d28f6b..e132157a33f62 100644 --- a/java/src/org/openqa/selenium/net/PortProber.java +++ b/java/src/org/openqa/selenium/net/PortProber.java @@ -119,7 +119,7 @@ private static boolean isFree(String bindHost, int port) { socket.setReuseAddress(true); socket.bind(new InetSocketAddress(bindHost, port)); return true; - } catch (Exception e) { + } catch (IOException | RuntimeException e) { return false; } } diff --git a/java/src/org/openqa/selenium/os/ExternalProcess.java b/java/src/org/openqa/selenium/os/ExternalProcess.java index b9c05f441a347..2bbb7ceb01b9c 100644 --- a/java/src/org/openqa/selenium/os/ExternalProcess.java +++ b/java/src/org/openqa/selenium/os/ExternalProcess.java @@ -317,8 +317,11 @@ public void shutdown() { */ public void shutdown(Duration timeout) { try { - if (process.supportsNormalTermination()) { - process.destroy(); + // use the handle to prevent closing the stdin, stdout, stderr streams + ProcessHandle handle = process.toHandle(); + + if (handle.supportsNormalTermination()) { + handle.destroy(); try { if (process.waitFor(timeout.toMillis(), MILLISECONDS)) { @@ -330,7 +333,7 @@ public void shutdown(Duration timeout) { } } - process.destroyForcibly(); + handle.destroyForcibly(); try { process.waitFor(timeout.toMillis(), MILLISECONDS); } catch (InterruptedException ex) { diff --git a/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java b/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java index d636048364d0d..03fc84bc45b19 100644 --- a/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java +++ b/java/src/org/openqa/selenium/remote/HttpCommandExecutor.java @@ -44,11 +44,11 @@ public class HttpCommandExecutor implements CommandExecutor, NeedsLocalLogs { private final URL remoteServer; - private final HttpClient client; + public final HttpClient client; private final HttpClient.Factory httpClientFactory; private final Map additionalCommands; - private CommandCodec commandCodec; - private ResponseCodec responseCodec; + protected CommandCodec commandCodec; + protected ResponseCodec responseCodec; private LocalLogs logs = LocalLogs.getNullLogger(); diff --git a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java index 4afa13ea9be83..58f08763b2ebd 100644 --- a/java/src/org/openqa/selenium/remote/RemoteWebDriver.java +++ b/java/src/org/openqa/selenium/remote/RemoteWebDriver.java @@ -118,7 +118,7 @@ public class RemoteWebDriver private Level level = Level.FINE; private ErrorHandler errorHandler = new ErrorHandler(); private CommandExecutor executor; - private Capabilities capabilities; + protected Capabilities capabilities; private SessionId sessionId; private FileDetector fileDetector = new UselessFileDetector(); private ExecuteMethod executeMethod; diff --git a/java/src/org/openqa/selenium/remote/codec/AbstractHttpResponseCodec.java b/java/src/org/openqa/selenium/remote/codec/AbstractHttpResponseCodec.java index b806dfc40935c..78b18f76e09d3 100644 --- a/java/src/org/openqa/selenium/remote/codec/AbstractHttpResponseCodec.java +++ b/java/src/org/openqa/selenium/remote/codec/AbstractHttpResponseCodec.java @@ -108,13 +108,6 @@ public Response decode(HttpResponse encodedResponse) { response.setValue(content); } - if (response.getValue() instanceof String) { - // We normalise to \n because Java will translate this to \r\n - // if this is suitable on our platform, and if we have \r\n, java will - // turn this into \r\r\n, which would be Bad! - response.setValue(((String) response.getValue()).replace("\r\n", "\n")); - } - if (response.getStatus() != null && response.getState() == null) { response.setState(errorCodes.toState(response.getStatus())); } else if (response.getStatus() == null && response.getState() != null) { 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..0fee3531ce6c0 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,64 @@ 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); + } + }); + + // try to interrupt the http request in case of a timeout, to avoid + // https://bugs.openjdk.org/browse/JDK-8258397 + cf.exceptionally( + (throwable) -> { + if (throwable instanceof java.util.concurrent.TimeoutException) { + // interrupts the thread + future.cancel(true); + } + + // nobody will read this result + return null; + }); + + // 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); + try { + // executeAsync does define a timeout, no need to use a timeout here + return executeAsync(req).get(); + } catch (CancellationException e) { + throw new WebDriverException(e.getMessage(), e); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + 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 +446,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,6 +489,8 @@ 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) { diff --git a/java/src/org/openqa/selenium/remote/service/DriverFinder.java b/java/src/org/openqa/selenium/remote/service/DriverFinder.java index 7216b5d9783d1..436878fbbb48f 100644 --- a/java/src/org/openqa/selenium/remote/service/DriverFinder.java +++ b/java/src/org/openqa/selenium/remote/service/DriverFinder.java @@ -156,6 +156,8 @@ private List toArguments() { arguments.add(proxy.getSslProxy()); } else if (proxy.getHttpProxy() != null) { arguments.add(proxy.getHttpProxy()); + } else if (proxy.getProxyAutoconfigUrl() != null) { + arguments.add(proxy.getProxyAutoconfigUrl()); } } return arguments; diff --git a/java/src/org/openqa/selenium/remote/service/DriverService.java b/java/src/org/openqa/selenium/remote/service/DriverService.java index 90fb1f2ca127d..0e0c04f693d9c 100644 --- a/java/src/org/openqa/selenium/remote/service/DriverService.java +++ b/java/src/org/openqa/selenium/remote/service/DriverService.java @@ -355,7 +355,7 @@ private enum StartOrDie { public abstract static class Builder> { private int port = 0; - private File exe = null; + public File exe = null; private Map environment = emptyMap(); private File logFile; private Duration timeout; diff --git a/java/src/org/openqa/selenium/support/ui/FluentWait.java b/java/src/org/openqa/selenium/support/ui/FluentWait.java index b8bd205c8b961..2c02e5f424557 100644 --- a/java/src/org/openqa/selenium/support/ui/FluentWait.java +++ b/java/src/org/openqa/selenium/support/ui/FluentWait.java @@ -66,15 +66,15 @@ public class FluentWait implements Wait { private static final Duration DEFAULT_WAIT_DURATION = Duration.ofMillis(DEFAULT_SLEEP_TIMEOUT); - private final T input; - private final java.time.Clock clock; - private final Sleeper sleeper; + protected final T input; + protected final java.time.Clock clock; + protected final Sleeper sleeper; - private Duration timeout = DEFAULT_WAIT_DURATION; - private Duration interval = DEFAULT_WAIT_DURATION; - private Supplier messageSupplier = () -> null; + protected Duration timeout = DEFAULT_WAIT_DURATION; + protected Duration interval = DEFAULT_WAIT_DURATION; + protected Supplier messageSupplier = () -> null; - private final List> ignoredExceptions = new ArrayList<>(); + protected final List> ignoredExceptions = new ArrayList<>(); /** * @param input The input value to pass to the evaluated conditions. 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/grid/distributor/local/LocalDistributorTest.java b/java/test/org/openqa/selenium/grid/distributor/local/LocalDistributorTest.java index eaba9689faa80..3ce2fe80d8352 100644 --- a/java/test/org/openqa/selenium/grid/distributor/local/LocalDistributorTest.java +++ b/java/test/org/openqa/selenium/grid/distributor/local/LocalDistributorTest.java @@ -102,6 +102,8 @@ public void setUp() throws URISyntaxException { LocalNode.builder(tracer, bus, uri, uri, registrationSecret) .add(caps, new TestSessionFactory((id, c) -> new Handler(c))) .maximumConcurrentSessions(2) + .sessionTimeout(Duration.ofSeconds(30)) + .heartbeatPeriod(Duration.ofSeconds(5)) .build(); wait = @@ -143,6 +145,8 @@ void testAddNodeToDistributor() { NodeStatus distributorNode = nodes.iterator().next(); assertThat(distributorNode.getNodeId()).isEqualByComparingTo(localNode.getId()); assertThat(distributorNode.getExternalUri()).isEqualTo(uri); + assertThat(distributorNode.getSessionTimeout()).isEqualTo(Duration.ofSeconds(30)); + assertThat(distributorNode.getHeartbeatPeriod()).isEqualTo(Duration.ofSeconds(5)); } @Test diff --git a/java/test/org/openqa/selenium/grid/gridui/OverallGridTest.java b/java/test/org/openqa/selenium/grid/gridui/OverallGridTest.java index 586230b88a402..b508370e27b20 100644 --- a/java/test/org/openqa/selenium/grid/gridui/OverallGridTest.java +++ b/java/test/org/openqa/selenium/grid/gridui/OverallGridTest.java @@ -68,7 +68,7 @@ public void tearDown() { @Test void shouldReportConcurrencyZeroPercentWhenGridIsStartedWithoutLoad() { - driver.get(whereIs(server, "/ui#/sessions")); + driver.get(whereIs(server, "/ui/#/sessions")); WebElement concurrency = wait.until( @@ -79,7 +79,7 @@ void shouldReportConcurrencyZeroPercentWhenGridIsStartedWithoutLoad() { @Test void shouldShowOneNodeRegistered() { - driver.get(whereIs(server, "/ui")); + driver.get(whereIs(server, "/ui/")); List nodeInfoIcons = wait.until( @@ -93,7 +93,7 @@ void shouldIncrementSessionCountWhenSessionStarts() { WebDriver remoteWebDriver = new RemoteWebDriver(server.getUrl(), Browser.detect().getCapabilities()); try { - driver.get(whereIs(server, "/ui#/sessions")); + driver.get(whereIs(server, "/ui/#/sessions")); wait.until(textToBe(By.cssSelector("div[data-testid='session-count']"), "1")); } finally { diff --git a/java/test/org/openqa/selenium/grid/node/ForwardWebDriverCommandTest.java b/java/test/org/openqa/selenium/grid/node/ForwardWebDriverCommandTest.java new file mode 100644 index 0000000000000..8f0df29251870 --- /dev/null +++ b/java/test/org/openqa/selenium/grid/node/ForwardWebDriverCommandTest.java @@ -0,0 +1,80 @@ +// 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 java.net.HttpURLConnection.HTTP_INTERNAL_ERROR; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.Mockito.*; +import static org.openqa.selenium.remote.http.Contents.asJson; + +import com.google.common.collect.ImmutableMap; +import java.util.UUID; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openqa.selenium.grid.data.NodeId; +import org.openqa.selenium.remote.SessionId; +import org.openqa.selenium.remote.http.HttpRequest; +import org.openqa.selenium.remote.http.HttpResponse; + +class ForwardWebDriverCommandTest { + + private Node mockNode; + private ForwardWebDriverCommand command; + + @BeforeEach + void setUp() { + mockNode = mock(Node.class); + when(mockNode.getId()).thenReturn(new NodeId(UUID.randomUUID())); + command = new ForwardWebDriverCommand(mockNode); + } + + @Test + void testExecuteWithValidSessionOwner() { + HttpRequest mockRequest = mock(HttpRequest.class); + when(mockRequest.getUri()).thenReturn("/session/1234"); + + SessionId sessionId = new SessionId("1234"); + when(mockNode.isSessionOwner(sessionId)).thenReturn(true); + + HttpResponse expectedResponse = new HttpResponse(); + when(mockNode.executeWebDriverCommand(mockRequest)).thenReturn(expectedResponse); + + HttpResponse actualResponse = command.execute(mockRequest); + assertEquals(expectedResponse, actualResponse); + } + + @Test + void testExecuteWithInvalidSessionOwner() { + HttpRequest mockRequest = mock(HttpRequest.class); + when(mockRequest.getUri()).thenReturn("/session/5678"); + + SessionId sessionId = new SessionId("5678"); + when(mockNode.isSessionOwner(sessionId)).thenReturn(false); + + HttpResponse actualResponse = command.execute(mockRequest); + HttpResponse expectResponse = + new HttpResponse() + .setStatus(HTTP_INTERNAL_ERROR) + .setContent( + asJson( + ImmutableMap.of( + "error", String.format("Session not found in node %s", mockNode.getId())))); + assertEquals(expectResponse.getStatus(), actualResponse.getStatus()); + assertEquals(expectResponse.getContentEncoding(), actualResponse.getContentEncoding()); + } +} diff --git a/java/test/org/openqa/selenium/grid/node/NodeTest.java b/java/test/org/openqa/selenium/grid/node/NodeTest.java index b1c50ed0dc65a..c2a66d2162c4b 100644 --- a/java/test/org/openqa/selenium/grid/node/NodeTest.java +++ b/java/test/org/openqa/selenium/grid/node/NodeTest.java @@ -23,6 +23,8 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.assertj.core.api.InstanceOfAssertFactories.MAP; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.openqa.selenium.json.Json.MAP_TYPE; import static org.openqa.selenium.remote.http.Contents.string; import static org.openqa.selenium.remote.http.HttpMethod.DELETE; @@ -102,7 +104,9 @@ class NodeTest { private Tracer tracer; private EventBus bus; private LocalNode local; + private LocalNode local2; private Node node; + private Node node2; private ImmutableCapabilities stereotype; private ImmutableCapabilities caps; private URI uri; @@ -150,6 +154,7 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException { builder = builder.enableManagedDownloads(true).sessionTimeout(Duration.ofSeconds(1)); } local = builder.build(); + local2 = builder.build(); node = new RemoteNode( @@ -160,6 +165,16 @@ public HttpResponse execute(HttpRequest req) throws UncheckedIOException { registrationSecret, local.getSessionTimeout(), ImmutableSet.of(caps)); + + node2 = + new RemoteNode( + tracer, + new PassthroughHttpClient.Factory(local2), + new NodeId(UUID.randomUUID()), + uri, + registrationSecret, + local2.getSessionTimeout(), + ImmutableSet.of(caps)); } @Test @@ -371,13 +386,36 @@ void shouldOnlyRespondToWebDriverCommandsForSessionsTheNodeOwns() { assertThatEither(response).isRight(); Session session = response.right().getSession(); + Either response2 = + node2.newSession(createSessionRequest(caps)); + assertThatEither(response2).isRight(); + Session session2 = response2.right().getSession(); + + // Assert that should respond to commands for sessions Node 1 owns HttpRequest req = new HttpRequest(POST, String.format("/session/%s/url", session.getId())); assertThat(local.matches(req)).isTrue(); assertThat(node.matches(req)).isTrue(); - req = new HttpRequest(POST, String.format("/session/%s/url", UUID.randomUUID())); - assertThat(local.matches(req)).isFalse(); - assertThat(node.matches(req)).isFalse(); + // Assert that should respond to commands for sessions Node 2 owns + HttpRequest req2 = new HttpRequest(POST, String.format("/session/%s/url", session2.getId())); + assertThat(local2.matches(req2)).isTrue(); + assertThat(node2.matches(req2)).isTrue(); + + // Assert that should not respond to commands for sessions Node 1 does not own + NoSuchSessionException exception = + assertThrows(NoSuchSessionException.class, () -> node.execute(req2)); + assertTrue( + exception + .getMessage() + .startsWith(String.format("Cannot find session with id: %s", session2.getId()))); + + // Assert that should not respond to commands for sessions Node 2 does not own + NoSuchSessionException exception2 = + assertThrows(NoSuchSessionException.class, () -> node2.execute(req)); + assertTrue( + exception2 + .getMessage() + .startsWith(String.format("Cannot find session with id: %s", session.getId()))); } @Test diff --git a/java/test/org/openqa/selenium/grid/node/config/NodeOptionsTest.java b/java/test/org/openqa/selenium/grid/node/config/NodeOptionsTest.java index 43d413cdab362..69f1ba30a9ee9 100644 --- a/java/test/org/openqa/selenium/grid/node/config/NodeOptionsTest.java +++ b/java/test/org/openqa/selenium/grid/node/config/NodeOptionsTest.java @@ -719,6 +719,50 @@ void settingSlotMatcherAvailable() { assertThat(nodeOptions.getSlotMatcher()).isExactlyInstanceOf(YesSlotMatcher.class); } + @Test + void testIsVncEnabledAcceptListEnvVarsAndReturnTrue() { + System.setProperty("SE_START_XVFB", "true"); + System.setProperty("SE_START_VNC", "true"); + System.setProperty("SE_START_NO_VNC", "true"); + String[] rawConfig = + new String[] { + "[node]", "vnc-env-var = [\"SE_START_XVFB\", \"SE_START_VNC\", \"SE_START_NO_VNC\"]", + }; + Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig))); + NodeOptions nodeOptionsEnabled = new NodeOptions(config); + assertThat(config.getAll("node", "vnc-env-var").get()) + .containsExactly("SE_START_XVFB", "SE_START_VNC", "SE_START_NO_VNC"); + assertThat(nodeOptionsEnabled.isVncEnabled()).isTrue(); + } + + @Test + void testIsVncEnabledAcceptListEnvVarsAndReturnFalse() { + System.setProperty("SE_START_XVFB", "true"); + System.setProperty("SE_START_VNC", "false"); + String[] rawConfig = + new String[] { + "[node]", "vnc-env-var = [\"SE_START_XVFB\", \"SE_START_VNC\", \"SE_START_NO_VNC\"]", + }; + Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig))); + NodeOptions nodeOptionsEnabled = new NodeOptions(config); + assertThat(config.getAll("node", "vnc-env-var").get()) + .containsExactly("SE_START_XVFB", "SE_START_VNC", "SE_START_NO_VNC"); + assertThat(nodeOptionsEnabled.isVncEnabled()).isFalse(); + } + + @Test + void testIsVncEnabledAcceptSingleEnvVar() { + System.setProperty("SE_START_XVFB", "false"); + String[] rawConfig = + new String[] { + "[node]", "vnc-env-var = \"SE_START_XVFB\"", + }; + Config config = new TomlConfig(new StringReader(String.join("\n", rawConfig))); + NodeOptions nodeOptionsEnabled = new NodeOptions(config); + assertThat(config.getAll("node", "vnc-env-var").get()).containsExactly("SE_START_XVFB"); + assertThat(nodeOptionsEnabled.isVncEnabled()).isFalse(); + } + private Condition> supporting(String name) { return new Condition<>( caps -> caps.stream().anyMatch(cap -> name.equals(cap.getBrowserName())), diff --git a/java/test/org/openqa/selenium/grid/router/StressTest.java b/java/test/org/openqa/selenium/grid/router/StressTest.java index a2c432229297f..3be76395e4f01 100644 --- a/java/test/org/openqa/selenium/grid/router/StressTest.java +++ b/java/test/org/openqa/selenium/grid/router/StressTest.java @@ -20,6 +20,8 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.concurrent.TimeUnit.MINUTES; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import java.io.StringReader; import java.util.LinkedList; @@ -33,7 +35,10 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.openqa.selenium.By; +import org.openqa.selenium.MutableCapabilities; +import org.openqa.selenium.NoSuchSessionException; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.WebDriverException; import org.openqa.selenium.grid.config.MapConfig; import org.openqa.selenium.grid.config.MemoizedConfig; import org.openqa.selenium.grid.config.TomlConfig; @@ -65,7 +70,14 @@ public void setupServers() { DeploymentTypes.DISTRIBUTED.start( browser.getCapabilities(), new TomlConfig( - new StringReader("[node]\n" + "driver-implementation = " + browser.displayName()))); + new StringReader( + "[node]\n" + + "driver-implementation = " + + browser.displayName() + + "\n" + + "session-timeout = 11" + + "\n" + + "enable-managed-downloads = true"))); tearDowns.add(deployment); server = deployment.getServer(); @@ -106,7 +118,10 @@ void multipleSimultaneousSessions() throws Exception { try { WebDriver driver = RemoteWebDriver.builder() - .oneOf(browser.getCapabilities()) + .oneOf( + browser + .getCapabilities() + .merge(new MutableCapabilities(Map.of("se:downloadsEnabled", true)))) .address(server.getUrl()) .build(); @@ -124,4 +139,44 @@ void multipleSimultaneousSessions() throws Exception { CompletableFuture.allOf(futures).get(4, MINUTES); } + + @Test + void multipleSimultaneousSessionsTimedOut() throws Exception { + assertThat(server.isStarted()).isTrue(); + + CompletableFuture[] futures = new CompletableFuture[10]; + for (int i = 0; i < futures.length; i++) { + CompletableFuture future = new CompletableFuture<>(); + futures[i] = future; + + executor.submit( + () -> { + try { + WebDriver driver = + RemoteWebDriver.builder() + .oneOf(browser.getCapabilities()) + .address(server.getUrl()) + .build(); + driver.get(appServer.getUrl().toString()); + Thread.sleep(11000); + NoSuchSessionException exception = + assertThrows(NoSuchSessionException.class, driver::getTitle); + assertTrue(exception.getMessage().startsWith("Cannot find session with id:")); + WebDriverException webDriverException = + assertThrows( + WebDriverException.class, + () -> ((RemoteWebDriver) driver).getDownloadableFiles()); + assertTrue( + webDriverException + .getMessage() + .startsWith("Cannot find downloads file system for session id:")); + future.complete(true); + } catch (Exception e) { + future.completeExceptionally(e); + } + }); + } + + CompletableFuture.allOf(futures).get(5, MINUTES); + } } diff --git a/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java b/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java index a50b4c11f75e3..3379772a8db1e 100644 --- a/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java +++ b/java/test/org/openqa/selenium/remote/internal/HttpClientTestBase.java @@ -35,6 +35,7 @@ import java.util.List; import java.util.Map; import java.util.TreeMap; +import java.util.concurrent.atomic.AtomicInteger; import java.util.logging.Logger; import java.util.stream.StreamSupport; import org.junit.jupiter.api.AfterAll; @@ -233,6 +234,35 @@ public void shouldAllowConfigurationFromSystemProperties() { } } + @Test + public void shouldStopRequestAfterTimeout() throws InterruptedException { + AtomicInteger counter = new AtomicInteger(); + + delegate = + req -> { + counter.incrementAndGet(); + try { + Thread.sleep(1600); + } catch (InterruptedException ex) { + Thread.currentThread().interrupt(); + } + HttpResponse response = new HttpResponse(); + response.setStatus(302); + response.addHeader("Location", "/"); + return response; + }; + ClientConfig clientConfig = ClientConfig.defaultConfig().readTimeout(Duration.ofMillis(800)); + + try (HttpClient client = + createFactory().createClient(clientConfig.baseUri(URI.create(server.whereIs("/"))))) { + HttpRequest request = new HttpRequest(GET, "/delayed"); + assertThatExceptionOfType(TimeoutException.class).isThrownBy(() -> client.execute(request)); + Thread.sleep(4200); + + assertThat(counter.get()).isEqualTo(1); + } + } + private HttpResponse getResponseWithHeaders(final Multimap headers) { return executeWithinServer( new HttpRequest(GET, "/foo"), diff --git a/java/version.bzl b/java/version.bzl index 5a9af5146a016..ccde019f14cbd 100644 --- a/java/version.bzl +++ b/java/version.bzl @@ -1,2 +1,2 @@ -SE_VERSION = "4.25.0" +SE_VERSION = "4.26.0" TOOLS_JAVA_VERSION = "17" diff --git a/javascript/grid-ui/package.json b/javascript/grid-ui/package.json index add2c75d45847..5a59007ec2986 100644 --- a/javascript/grid-ui/package.json +++ b/javascript/grid-ui/package.json @@ -4,13 +4,13 @@ "private": true, "type": "module", "dependencies": { - "@apollo/client": "3.10.4", - "@emotion/react": "11.11.4", - "@emotion/styled": "11.11.5", + "@apollo/client": "3.11.8", + "@emotion/react": "11.13.3", + "@emotion/styled": "11.13.0", "@mui/icons-material": "5.15.18", "@mui/material": "5.15.18", "@novnc/novnc": "1.4.0", - "@types/jest": "29.5.12", + "@types/jest": "29.5.14", "@types/node": "20.12.12", "@types/react": "18.2.72", "@types/react-dom": "18.2.22", @@ -20,10 +20,10 @@ "graphql.macro": "1.4.2", "path-browserify": "1.0.1", "pretty-ms": "9.0.0", - "react": "18.2.0", - "react-dom": "18.2.0", + "react": "18.3.1", + "react-dom": "18.3.1", "react-modal": "3.16.1", - "react-router-dom": "6.22.3", + "react-router-dom": "6.27.0", "source-map-explorer": "2.5.3" }, "scripts": { @@ -47,13 +47,13 @@ ] }, "devDependencies": { - "@babel/preset-react": "7.24.7", - "@testing-library/jest-dom": "6.4.5", + "@babel/preset-react": "7.25.9", + "@testing-library/jest-dom": "6.6.2", "@testing-library/react": "14.3.1", "@testing-library/user-event": "14.5.2", - "esbuild": "0.19.12", + "esbuild": "0.24.0", "ts-standard": "12.0.2", - "typescript": "5.4.5" + "typescript": "5.6.3" }, "jest": { "testMatch": [ diff --git a/javascript/grid-ui/src/components/LiveView/LiveView.tsx b/javascript/grid-ui/src/components/LiveView/LiveView.tsx index b1d4ea1113811..503edbaa5e54d 100644 --- a/javascript/grid-ui/src/components/LiveView/LiveView.tsx +++ b/javascript/grid-ui/src/components/LiveView/LiveView.tsx @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -import React, { useEffect, useState } from 'react' +import React, { useEffect, useState, useImperativeHandle, forwardRef } from 'react' import RFB from '@novnc/novnc/core/rfb' import PasswordDialog from './PasswordDialog' import MuiAlert, { AlertProps } from '@mui/material/Alert' @@ -28,7 +28,7 @@ const Alert = React.forwardRef(function Alert ( return }) -function LiveView (props) { +const LiveView = forwardRef((props, ref) => { let canvas: any = null const [open, setOpen] = useState(false) @@ -49,6 +49,10 @@ function LiveView (props) { setRfb(null) } + useImperativeHandle(ref, () => ({ + disconnect + })) + const connect = () => { disconnect() @@ -109,6 +113,7 @@ function LiveView (props) { } const handlePasswordDialogClose = () => { + disconnect() props.onClose() } @@ -132,6 +137,7 @@ function LiveView (props) { return } setOpenErrorAlert(false) + disconnect() props.onClose() } @@ -176,6 +182,6 @@ function LiveView (props) { ) -} +}) export default LiveView diff --git a/javascript/grid-ui/src/components/Node/Node.tsx b/javascript/grid-ui/src/components/Node/Node.tsx index f61b2ec550288..79b2ced05f2a9 100644 --- a/javascript/grid-ui/src/components/Node/Node.tsx +++ b/javascript/grid-ui/src/components/Node/Node.tsx @@ -24,29 +24,28 @@ import OsLogo from '../common/OsLogo' function Node (props) { const { node } = props - const nodeStatusDown = node.status === 'DOWN' + const getCardStyle = (status: string) => ({ + height: '100%', + flexGrow: 1, + opacity: status === 'DOWN' ? 0.25 : 1, + bgcolor: (status === 'DOWN' || status === 'DRAINING') ? 'grey.A100' : '' + }) return ( - + - + URI: {node.uri} @@ -54,19 +53,19 @@ function Node (props) { - - + + - + - + diff --git a/javascript/grid-ui/src/components/RunningSessions/RunningSessions.tsx b/javascript/grid-ui/src/components/RunningSessions/RunningSessions.tsx index fcc666f3457c4..43fd2c1138f83 100644 --- a/javascript/grid-ui/src/components/RunningSessions/RunningSessions.tsx +++ b/javascript/grid-ui/src/components/RunningSessions/RunningSessions.tsx @@ -15,7 +15,7 @@ // specific language governing permissions and limitations // under the License. -import React, { useState } from 'react' +import React, { useState, useRef } from 'react' import Table from '@mui/material/Table' import TableBody from '@mui/material/TableBody' import TableCell from '@mui/material/TableCell' @@ -52,6 +52,9 @@ import LiveView from '../LiveView/LiveView' import SessionData, { createSessionData } from '../../models/session-data' function descendingComparator (a: T, b: T, orderBy: keyof T): number { + if (orderBy === 'sessionDurationMillis') { + return Number(b[orderBy]) - Number(a[orderBy]) + } if (b[orderBy] < a[orderBy]) { return -1 } @@ -94,7 +97,7 @@ const headCells: HeadCell[] = [ { id: 'id', numeric: false, label: 'Session' }, { id: 'capabilities', numeric: false, label: 'Capabilities' }, { id: 'startTime', numeric: false, label: 'Start time' }, - { id: 'sessionDurationMillis', numeric: false, label: 'Duration' }, + { id: 'sessionDurationMillis', numeric: true, label: 'Duration' }, { id: 'nodeUri', numeric: false, label: 'Node URI' } ] @@ -170,13 +173,21 @@ function RunningSessions (props) { const [rowOpen, setRowOpen] = useState('') const [rowLiveViewOpen, setRowLiveViewOpen] = useState('') const [order, setOrder] = useState('asc') - const [orderBy, setOrderBy] = useState('startTime') + const [orderBy, setOrderBy] = useState('sessionDurationMillis') const [selected, setSelected] = useState([]) const [page, setPage] = useState(0) const [dense, setDense] = useState(false) const [rowsPerPage, setRowsPerPage] = useState(10) const [searchFilter, setSearchFilter] = useState('') const [searchBarHelpOpen, setSearchBarHelpOpen] = useState(false) + const liveViewRef = useRef(null) + + const handleDialogClose = () => { + if (liveViewRef.current) { + liveViewRef.current.disconnect() + } + setRowLiveViewOpen('') + } const handleRequestSort = (event: React.MouseEvent, property: keyof SessionData) => { @@ -379,6 +390,7 @@ function RunningSessions (props) { sx={{ height: '600px' }} > setRowLiveViewOpen('')} @@ -386,7 +398,7 @@ function RunningSessions (props) {