From 6d4ebace8f32dfc5d9f1b6767fb08eb012eebd95 Mon Sep 17 00:00:00 2001 From: adiohana Date: Mon, 20 May 2019 16:05:08 +0300 Subject: [PATCH] Dev tools Network and Performance (#7212) Expose CDP primitives for Network and Performance domains. --- common/src/web/postForm.html | 14 + .../src/org/openqa/selenium/devtools/BUCK | 5 +- .../org/openqa/selenium/devtools/BUILD.bazel | 2 +- .../org/openqa/selenium/devtools/Command.java | 1 - .../org/openqa/selenium/devtools/Runtime.java | 4 + .../selenium/devtools/network/Network.java | 611 ++++++++++++++++++ .../devtools/network/model/AuthChallenge.java | 105 +++ .../network/model/AuthChallengeResponse.java | 78 +++ .../devtools/network/model/BlockedReason.java | 53 ++ .../devtools/network/model/CallFrame.java | 151 +++++ .../CertificateTransparencyCompliance.java | 48 ++ .../network/model/ConnectionType.java | 35 + .../devtools/network/model/Cookie.java | 146 +++++ .../network/model/CookieSameSite.java | 30 + .../devtools/network/model/Cookies.java | 59 ++ .../devtools/network/model/DataReceived.java | 111 ++++ .../devtools/network/model/ErrorReason.java | 40 ++ .../model/EventSourceMessageReceived.java | 127 ++++ .../devtools/network/model/Initiator.java | 136 ++++ .../devtools/network/model/InitiatorType.java | 29 + .../network/model/InterceptionId.java | 53 ++ .../network/model/InterceptionStage.java | 27 + .../devtools/network/model/LoaderId.java | 53 ++ .../devtools/network/model/LoadingFailed.java | 146 +++++ .../network/model/LoadingFinished.java | 114 ++++ .../network/model/MixedContentType.java | 49 ++ .../devtools/network/model/MonotonicTime.java | 41 ++ .../devtools/network/model/Request.java | 267 ++++++++ .../devtools/network/model/RequestId.java | 57 ++ .../network/model/RequestIntercepted.java | 230 +++++++ .../network/model/RequestPattern.java | 82 +++ .../network/model/RequestReferrerPolicy.java | 53 ++ .../network/model/RequestWillBeSent.java | 235 +++++++ .../model/ResourceChangedPriority.java | 95 +++ .../network/model/ResourcePriority.java | 31 + .../network/model/ResourceTiming.java | 222 +++++++ .../devtools/network/model/ResourceType.java | 42 ++ .../devtools/network/model/Response.java | 486 ++++++++++++++ .../devtools/network/model/ResponseBody.java | 80 +++ .../network/model/ResponseReceived.java | 139 ++++ .../devtools/network/model/SearchMatch.java | 51 ++ .../network/model/SecurityDetails.java | 369 +++++++++++ .../devtools/network/model/SecurityState.java | 31 + .../model/SignedCertificateTimestamp.java | 238 +++++++ .../network/model/SignedExchangeError.java | 107 +++ .../model/SignedExchangeErrorField.java | 32 + .../network/model/SignedExchangeHeader.java | 177 +++++ .../network/model/SignedExchangeInfo.java | 107 +++ .../network/model/SignedExchangeReceived.java | 100 +++ .../model/SignedExchangeSignature.java | 261 ++++++++ .../devtools/network/model/Source.java | 32 + .../devtools/network/model/StackTrace.java | 136 ++++ .../devtools/network/model/StackTraceId.java | 45 ++ .../network/model/WebSocketClosed.java | 64 ++ .../network/model/WebSocketCreated.java | 82 +++ .../network/model/WebSocketFrame.java | 85 +++ .../network/model/WebSocketFrameError.java | 71 ++ .../devtools/performance/Performance.java | 78 +++ .../devtools/performance/model/Metric.java | 51 ++ .../performance/model/TimeDomain.java | 23 + .../selenium/json/JsonInputConverter.java | 45 ++ .../test/org/openqa/selenium/I18nTest.java | 4 +- .../test/org/openqa/selenium/chrome/BUCK | 2 + .../selenium/chrome/ChromeDriverTests.java | 5 +- .../test/org/openqa/selenium/devtools/BUCK | 14 + .../devtools/ChromeDevToolsNetworkTest.java | 359 ++++++++++ .../ChromeDevToolsPerformanceTest.java | 95 +++ .../devtools/ChromeDevToolsTestBase.java | 49 ++ .../selenium/devtools/DevToolsTest.java | 52 -- .../selenium/devtools/DevToolsTestBase.java | 44 ++ .../selenium/devtools/DevToolsTests.java | 30 + .../selenium/grid/config/AnnotatedConfig.java | 2 +- 72 files changed, 7068 insertions(+), 60 deletions(-) create mode 100644 common/src/web/postForm.html create mode 100644 java/client/src/org/openqa/selenium/devtools/network/Network.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/AuthChallenge.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/AuthChallengeResponse.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/BlockedReason.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/CallFrame.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/CertificateTransparencyCompliance.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/ConnectionType.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/Cookie.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/CookieSameSite.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/Cookies.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/DataReceived.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/ErrorReason.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/EventSourceMessageReceived.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/Initiator.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/InitiatorType.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/InterceptionId.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/InterceptionStage.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/LoaderId.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/LoadingFailed.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/LoadingFinished.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/MixedContentType.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/MonotonicTime.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/Request.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/RequestId.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/RequestIntercepted.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/RequestPattern.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/RequestReferrerPolicy.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/RequestWillBeSent.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/ResourceChangedPriority.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/ResourcePriority.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/ResourceTiming.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/ResourceType.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/Response.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/ResponseBody.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/ResponseReceived.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/SearchMatch.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/SecurityDetails.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/SecurityState.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/SignedCertificateTimestamp.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeError.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeErrorField.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeHeader.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeInfo.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeReceived.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeSignature.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/Source.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/StackTrace.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/StackTraceId.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/WebSocketClosed.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/WebSocketCreated.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/WebSocketFrame.java create mode 100644 java/client/src/org/openqa/selenium/devtools/network/model/WebSocketFrameError.java create mode 100644 java/client/src/org/openqa/selenium/devtools/performance/Performance.java create mode 100644 java/client/src/org/openqa/selenium/devtools/performance/model/Metric.java create mode 100644 java/client/src/org/openqa/selenium/devtools/performance/model/TimeDomain.java create mode 100644 java/client/src/org/openqa/selenium/json/JsonInputConverter.java create mode 100644 java/client/test/org/openqa/selenium/devtools/BUCK create mode 100644 java/client/test/org/openqa/selenium/devtools/ChromeDevToolsNetworkTest.java create mode 100644 java/client/test/org/openqa/selenium/devtools/ChromeDevToolsPerformanceTest.java create mode 100644 java/client/test/org/openqa/selenium/devtools/ChromeDevToolsTestBase.java delete mode 100644 java/client/test/org/openqa/selenium/devtools/DevToolsTest.java create mode 100644 java/client/test/org/openqa/selenium/devtools/DevToolsTestBase.java create mode 100644 java/client/test/org/openqa/selenium/devtools/DevToolsTests.java diff --git a/common/src/web/postForm.html b/common/src/web/postForm.html new file mode 100644 index 0000000000000..2b2348e99ba48 --- /dev/null +++ b/common/src/web/postForm.html @@ -0,0 +1,14 @@ + + + +
+ + + +
+ + + diff --git a/java/client/src/org/openqa/selenium/devtools/BUCK b/java/client/src/org/openqa/selenium/devtools/BUCK index 209b32953b6a8..ad2e692e4356e 100644 --- a/java/client/src/org/openqa/selenium/devtools/BUCK +++ b/java/client/src/org/openqa/selenium/devtools/BUCK @@ -1,6 +1,6 @@ java_library( name = "devtools", - srcs = glob(["*.java"]), + srcs = glob(["**/*.java"]), deps = [ "//java/client/src/org/openqa/selenium:selenium", "//java/client/src/org/openqa/selenium/json:json", @@ -9,5 +9,6 @@ java_library( ], visibility = [ "//java/client/src/org/openqa/selenium/remote:", + "//java/client/test/...", ], -) \ No newline at end of file +) diff --git a/java/client/src/org/openqa/selenium/devtools/BUILD.bazel b/java/client/src/org/openqa/selenium/devtools/BUILD.bazel index ecdf9f826c62e..9c5390eeef90e 100644 --- a/java/client/src/org/openqa/selenium/devtools/BUILD.bazel +++ b/java/client/src/org/openqa/selenium/devtools/BUILD.bazel @@ -1,6 +1,6 @@ java_library( name = "devtools", - srcs = glob(["*.java"]), + srcs = glob(["**/*.java"]), visibility = [ "//java/client/src/org/openqa/selenium/chrome:__pkg__", "//java/client/src/org/openqa/selenium/remote:__pkg__", diff --git a/java/client/src/org/openqa/selenium/devtools/Command.java b/java/client/src/org/openqa/selenium/devtools/Command.java index 3821be4305e97..3226495d3a6eb 100644 --- a/java/client/src/org/openqa/selenium/devtools/Command.java +++ b/java/client/src/org/openqa/selenium/devtools/Command.java @@ -24,7 +24,6 @@ import java.lang.reflect.Type; import java.util.Map; import java.util.Objects; -import java.util.concurrent.atomic.AtomicLong; import java.util.function.Function; public class Command { diff --git a/java/client/src/org/openqa/selenium/devtools/Runtime.java b/java/client/src/org/openqa/selenium/devtools/Runtime.java index 93ad5df0fe126..8770fb7d06614 100644 --- a/java/client/src/org/openqa/selenium/devtools/Runtime.java +++ b/java/client/src/org/openqa/selenium/devtools/Runtime.java @@ -60,6 +60,10 @@ private static Timestamp fromJson(long timestamp) { return new Timestamp(timestamp); } + public static Timestamp fromJson(Number timestamp) { + return fromJson(timestamp.longValue()); + } + private long toJson() { return epochMillis; } diff --git a/java/client/src/org/openqa/selenium/devtools/network/Network.java b/java/client/src/org/openqa/selenium/devtools/network/Network.java new file mode 100644 index 0000000000000..b203a92e6084f --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/Network.java @@ -0,0 +1,611 @@ +// 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.devtools.network; + +import static org.openqa.selenium.devtools.ConverterFunctions.map; + +import com.google.common.collect.ImmutableMap; +import com.google.common.reflect.TypeToken; + +import org.openqa.selenium.Beta; +import org.openqa.selenium.devtools.Command; +import org.openqa.selenium.devtools.Event; +import org.openqa.selenium.devtools.network.model.AuthChallengeResponse; +import org.openqa.selenium.devtools.network.model.ConnectionType; +import org.openqa.selenium.devtools.network.model.Cookie; +import org.openqa.selenium.devtools.network.model.Cookies; +import org.openqa.selenium.devtools.network.model.DataReceived; +import org.openqa.selenium.devtools.network.model.ErrorReason; +import org.openqa.selenium.devtools.network.model.EventSourceMessageReceived; +import org.openqa.selenium.devtools.network.model.InterceptionId; +import org.openqa.selenium.devtools.network.model.LoadingFailed; +import org.openqa.selenium.devtools.network.model.LoadingFinished; +import org.openqa.selenium.devtools.network.model.RequestId; +import org.openqa.selenium.devtools.network.model.RequestIntercepted; +import org.openqa.selenium.devtools.network.model.RequestPattern; +import org.openqa.selenium.devtools.network.model.RequestWillBeSent; +import org.openqa.selenium.devtools.network.model.ResourceChangedPriority; +import org.openqa.selenium.devtools.network.model.ResponseBody; +import org.openqa.selenium.devtools.network.model.ResponseReceived; +import org.openqa.selenium.devtools.network.model.SearchMatch; +import org.openqa.selenium.devtools.network.model.SignedExchangeReceived; +import org.openqa.selenium.devtools.network.model.WebSocketClosed; +import org.openqa.selenium.devtools.network.model.WebSocketCreated; +import org.openqa.selenium.devtools.network.model.WebSocketFrame; +import org.openqa.selenium.devtools.network.model.WebSocketFrameError; + +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +/** + * All available DevTools Network methods and events + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class Network { + + private final static String DOMAIN_NAME = "Network"; + + public static Command clearBrowserCache() { + return new Command<>(DOMAIN_NAME + ".clearBrowserCache", ImmutableMap.of()); + } + + public static Command clearBrowserCookies() { + return new Command<>(DOMAIN_NAME + ".clearBrowserCookies", ImmutableMap.of()); + } + + /** + * Response to Network.requestIntercepted which either modifies the request to continue with any modifications, or blocks it, or completes it with the provided response bytes. + * If a network fetch occurs as a result which encounters a redirect an additional Network.requestIntercepted event will be sent with the same InterceptionId. + * (EXPERIMENTAL) + * + * @param interceptionId - Identifier for the intercepted request + * @param errorReason (Optional) - If set this causes the request to fail with the given reason. + * Passing Aborted for requests marked with isNavigationRequest also cancels the navigation. Must not be set in response to an authChallenge + * @param rawResponse (Optional) - If set the requests completes using with the provided base64 encoded raw response, including HTTP status line and headers etc... + * Must not be set in response to an authChallenge + * @param url (Optional) - If set the request url will be modified in a way that's not observable by page. Must not be set in response to an authChallenge + * @param method (Optional) - If set this allows the request method to be overridden. Must not be set in response to an authChallenge + * @param postData (Optional) - If set this allows postData to be set. Must not be set in response to an authChallenge + * @param headers (Optional) - If set this allows the request headers to be changed. Must not be set in response to an authChallenge + * @param authChallengeResponse (Optional) - Response to a requestIntercepted with an authChallenge. Must not be set otherwise + * @return DevTools Command + */ + @Beta + public static Command continueInterceptedRequest(InterceptionId interceptionId, + Optional errorReason, + Optional rawResponse, + Optional url, + Optional method, + Optional postData, + Optional> headers, + Optional authChallengeResponse) { + + Objects.requireNonNull(interceptionId, "interceptionId must be set."); + + final ImmutableMap.Builder params = ImmutableMap.builder(); + + params.put("interceptionId", interceptionId.toString()); + errorReason.ifPresent(reason -> params.put("errorReason", errorReason.get().name())); + rawResponse.ifPresent(string -> params.put("rawResponse", rawResponse.toString())); + url.ifPresent(string -> params.put("url", url.toString())); + method.ifPresent(string -> params.put("method", method.toString())); + postData.ifPresent(string -> params.put("postData", postData.toString())); + headers.ifPresent(map -> params.put("headers", headers)); + authChallengeResponse.ifPresent(response -> params.put("authChallengeResponse", authChallengeResponse)); + + return new Command<>(DOMAIN_NAME + ".continueInterceptedRequest", params.build()); + + } + + /** + * Deletes browser cookies with matching name and url or domain/path pair + * + * @param name - Name of the cookies to remove + * @param url (Optional) - If specified, deletes all the cookies with the given name where domain and path match provided URL + * @param domain (Optional) - If specified, deletes only cookies with the exact domain. + * @param path (Optional) - If specified, deletes only cookies with the exact path + * @return DevTools Command + */ + public static Command deleteCookies(String name, Optional url, + Optional domain, Optional path) { + + Objects.requireNonNull(name, "name must be set."); + + final ImmutableMap.Builder params = ImmutableMap.builder(); + + params.put("name", name); + url.ifPresent(string -> params.put("url", url.toString())); + domain.ifPresent(string -> params.put("domain", domain.toString())); + path.ifPresent(string -> params.put("path", path.toString())); + + return new Command<>(DOMAIN_NAME + ".deleteCookies", params.build()); + + } + + /** + * Disables network tracking, prevents network events from being sent to the client. + * + * @return DevTools Command + */ + public static Command disable() { + return new Command<>(DOMAIN_NAME + ".disable", ImmutableMap.of()); + } + + /** + * Activates emulation of network conditions. + * + * @param offline - True to emulate internet disconnection. + * @param latency - Minimum latency from request sent to response headers received (ms). + * @param downloadThroughput - Maximal aggregated download throughput (bytes/sec). -1 disables download throttling. + * @param uploadThroughput - Maximal aggregated upload throughput (bytes/sec). -1 disables upload throttling. + * @param connectionType (Optional) - The underlying connection technology that the browser is supposedly using. + * @return DevTools Command + */ + public static Command emulateNetworkConditions(boolean offline, double latency, + double downloadThroughput, + double uploadThroughput, + Optional connectionType) { + + final ImmutableMap.Builder params = ImmutableMap.builder(); + + params.put("offline", offline); + params.put("latency", latency); + params.put("downloadThroughput", downloadThroughput); + params.put("uploadThroughput", uploadThroughput); + + connectionType + .ifPresent(ConnectionType -> params.put("connectionType", connectionType.get().name())); + + return new Command<>(DOMAIN_NAME + ".emulateNetworkConditions", params.build()); + + } + + /** + * Enables network tracking, network events will now be delivered to the client. + * + * @param maxTotalBufferSize (Optional) - Buffer size in bytes to use when preserving network payloads (XHRs, etc). + * @param maxResourceBufferSize (Optional) - Per-resource buffer size in bytes to use when preserving network payloads (XHRs, etc). + * @param maxPostDataSize (Optional) - Longest post body size (in bytes) that would be included in requestWillBeSent notification + * @return DevTools Command + */ + public static Command enable(Optional maxTotalBufferSize, + Optional maxResourceBufferSize, + Optional maxPostDataSize) { + + final ImmutableMap.Builder params = ImmutableMap.builder(); + + maxTotalBufferSize.ifPresent(integer -> params.put("maxTotalBufferSize", integer)); + maxResourceBufferSize.ifPresent(integer -> params.put("maxResourceBufferSize", integer)); + maxPostDataSize.ifPresent(integer -> params.put("maxPostDataSize", integer)); + + return new Command<>(DOMAIN_NAME + ".enable", params.build()); + + } + + /** + * Returns all browser cookies. Depending on the backend support, will return detailed cookie information in the cookies field + * + * @return Array of Cookies with a "asSeleniumCookies" method + */ + public static Command getAllCookies() { + return new Command<>(DOMAIN_NAME + ".getAllCookies", ImmutableMap.of(), + map("cookies", Cookies.class)); + } + + /** + * Returns the DER-encoded certificate (EXPERIMENTAL) + * + * @param origin Origin to get certificate for + * @return List of tableNames + */ + @Beta + public static Command> getCertificate(String origin) { + Objects.requireNonNull(origin, "origin must be set."); + return new Command<>(DOMAIN_NAME + ".getCertificate", ImmutableMap.of("origin", origin), + map("tableNames", new TypeToken>() { + }.getType())); + } + + /** + * Returns all browser cookies for the current URL. Depending on the backend support, will return detailed cookie information in the cookies field + * + * @param urls (Optional) - The list of URLs for which applicable cookies will be fetched + * @return Array of cookies + */ + public static Command getCookies(Optional> urls) { + + final ImmutableMap.Builder params = ImmutableMap.builder(); + + urls.ifPresent(list -> params.put("urls", urls)); + + return new Command<>(DOMAIN_NAME + ".getCookies", params.build(), + map("cookies", Cookies.class)); + + } + + /** + * Returns content served for the given request + * + * @param requestId Identifier of the network request to get content for + * @return ResponseBody object + */ + public static Command getResponseBody(RequestId requestId) { + Objects.requireNonNull(requestId, "requestId must be set."); + return new Command<>(DOMAIN_NAME + ".getResponseBody", + ImmutableMap.of("requestId", requestId.toString()), + map("body", ResponseBody.class)); + } + + /** + * Returns post data sent with the request. Returns an error when no data was sent with the request. + * + * @param requestId - Identifier of the network request to get content for. + * @return DevTools Command with Request body string, omitting files from multipart requests + */ + public static Command getRequestPostData(RequestId requestId) { + Objects.requireNonNull(requestId, "requestId must be set."); + return new Command<>(DOMAIN_NAME + ".getRequestPostData", + ImmutableMap.of("requestId", requestId.toString()), + map("postData", String.class)); + } + + /** + * Returns content served for the given currently intercepted request (EXPERIMENTAL) + * + * @param interceptionId - Identifier for the intercepted request to get body for + * @return ResponseBody object + */ + @Beta + public static Command getResponseBodyForInterception( + InterceptionId interceptionId) { + Objects.requireNonNull(interceptionId.toString(), "interceptionId must be set."); + return new Command<>(DOMAIN_NAME + ".getResponseBodyForInterception", + ImmutableMap.of("interceptionId", interceptionId), + map("body", ResponseBody.class)); + } + + /** + * Returns a handle to the stream representing the response body. Note that after this command, the intercepted request can't be continued as is -- you either need to cancel it or to provide the response body. + * The stream only supports sequential read, IO.read will fail if the position is specified (EXPERIMENTAL) + * + * @param interceptionId - Identifier for the intercepted request to get body for + * @return HTTP response body Stream as a String + */ + @Beta + public static Command takeResponseBodyForInterceptionAsStream( + InterceptionId interceptionId) { + Objects.requireNonNull(interceptionId, "interceptionId must be set."); + return new Command<>(DOMAIN_NAME + ".takeResponseBodyForInterceptionAsStream", + ImmutableMap.of("interceptionId", interceptionId), + map("stream", String.class)); + } + + /** + * @param requestId - Identifier of XHR to replay + * @return - DevTools Command + */ + public static Command replayXHR(RequestId requestId) { + + Objects.requireNonNull(requestId, "requestId must be set."); + return new Command<>(DOMAIN_NAME + ".replayXHR", ImmutableMap.of("requestId", requestId.toString())); + + } + + /** + * Searches for given string in response content (EXPERIMENTAL) + * + * @param requestId - Identifier of the network response to search + * @param query - String to search for. + * @param caseSensitive - If true, search is case sensitive + * @param isRegex - If true, treats string parameter as regex + * @return List of SearchMatch + */ + @Beta + public static Command> searchInResponseBody(RequestId requestId, String query, + Optional caseSensitive, + Optional isRegex) { + + Objects.requireNonNull(requestId, "requestId must be set."); + Objects.requireNonNull(query, "query must be set."); + + final ImmutableMap.Builder params = ImmutableMap.builder(); + + params.put("requestId", requestId.toString()); + params.put("query", query); + caseSensitive.ifPresent(bool -> params.put("caseSensitive", caseSensitive)); + isRegex.ifPresent(bool -> params.put("isRegex", isRegex)); + + return new Command<>(DOMAIN_NAME + ".searchInResponseBody", params.build(), + map("result", new TypeToken>() { + }.getType())); + } + + /** + * Blocks URLs from loading (EXPERIMENTAL) + * + * @param urls - URL patterns to block. Wildcards ('*') are allowed. + * @return DevTools Command + */ + @Beta + public static Command setBlockedURLs(List urls) { + Objects.requireNonNull(urls, "urls must be set."); + return new Command<>(DOMAIN_NAME + ".setBlockedURLs", ImmutableMap.of("urls", urls)); + } + + /** + * Toggles ignoring of service worker for each request. (EXPERIMENTAL) + * + * @param bypass - Bypass service worker and load from network + * @return - DevTools Command + */ + @Beta + public static Command setBypassServiceWorker(boolean bypass) { + return new Command<>(DOMAIN_NAME + ".setBypassServiceWorker", + ImmutableMap.of("bypass", bypass)); + } + + /** + * Toggles ignoring cache for each request. If true, cache will not be used. + * + * @param cacheDisabled - Cache disabled state. + * @return DevTools Command + */ + public static Command setCacheDisabled(boolean cacheDisabled) { + return new Command<>(DOMAIN_NAME + ".setCacheDisabled", + ImmutableMap.of("cacheDisabled", cacheDisabled)); + } + + /** + * implementation using CDP Cookie + */ + private static Command setCookie(Cookie cookie, Optional url) { + Objects.requireNonNull(cookie.getName(), "cookieName must be set."); + Objects.requireNonNull(cookie.getValue(), "cookieValue must be set."); + + final ImmutableMap.Builder params = ImmutableMap.builder(); + + params.put("name", cookie.getName()); + params.put("value", cookie.getValue()); + url.ifPresent(string -> params.put("url", url.toString())); + + if (cookie.getDomain() != null) { + params.put("domain", cookie.getDomain()); + } + if (cookie.getPath() != null) { + params.put("path", cookie.getPath()); + } + params.put("secure", cookie.isSecure()); + + params.put("httpOnly", cookie.isHttpOnly()); + + if (cookie.getExpires() != 0) { + params.put("expires", cookie.getExpires()); + } + + return new Command<>(DOMAIN_NAME + ".setCookie", params.build(), map("success", Boolean.class)); + } + + /** + * Sets a cookie with the given cookie data; may overwrite equivalent cookies if they exist + * + * @param cookie - Cookie object where Name and Value are mandatory + * @param url - The request-URI to associate with the setting of the cookie. This value can affect the default domain and path values of the created cookie + * @return - Boolean + */ + public static Command setCookie(org.openqa.selenium.Cookie cookie, + Optional url) { + return setCookie(Cookie.fromSeleniumCookie(cookie), url); + } + + /** + * (EXPERIMENTAL) + * + * @param maxTotalSize - Maximum total buffer size + * @param maxResourceSize - Maximum per-resource size + * @return DevTools Command + */ + @Beta + public static Command setDataSizeLimitsForTest(int maxTotalSize, int maxResourceSize) { + return new Command<>(DOMAIN_NAME + ".setDataSizeLimitsForTest", ImmutableMap + .of("maxTotalSize", maxTotalSize, "maxResourceSize", maxResourceSize)); + } + + /** + * Specifies whether to always send extra HTTP headers with the requests from this page. + * + * @param headers - Map with extra HTTP headers. + * @return DevTools Command + */ + public static Command setExtraHTTPHeaders(Map headers) { + Objects.requireNonNull(headers, "headers must be set."); + return new Command<>(DOMAIN_NAME + ".setExtraHTTPHeaders", ImmutableMap.of("headers", headers)); + } + + /** + * Sets the requests to intercept that match the provided patterns and optionally resource types (EXPERIMENTAL) + * + * @param patterns - Requests matching any of these patterns will be forwarded and wait for the corresponding continueInterceptedRequest call. + * @return DevTools Command + */ + @Beta + public static Command setRequestInterception(List patterns) { + Objects.requireNonNull(patterns, "patterns must be set."); + return new Command<>(DOMAIN_NAME + ".setRequestInterception", + ImmutableMap.of("patterns", patterns)); + } + + /** + * Allows overriding user agent with the given string + * + * @param userAgent - User agent to use + * @param acceptLanguage - Browser langugage to emulate + * @param platform - The platform navigator.platform should return + * @return DevTools Command + */ + public static Command setUserAgentOverride(String userAgent, + Optional acceptLanguage, + Optional platform) { + + Objects.requireNonNull(userAgent, "userAgent must be set."); + final ImmutableMap.Builder params = ImmutableMap.builder(); + + params.put("userAgent", userAgent); + acceptLanguage.ifPresent(string -> params.put("acceptLanguage", acceptLanguage.toString())); + platform.ifPresent(string -> params.put("platform", platform.toString())); + + return new Command<>(DOMAIN_NAME + ".setUserAgentOverride", params.build()); + } + + /** + * Fired when data chunk was received over the network. + * + * @return DataReceived Event + */ + public static Event dataReceived() { + return new Event<>(DOMAIN_NAME + ".dataReceived", map("requestId", DataReceived.class)); + } + + /** + * Fired when EventSource message is received + * + * @return EventSourceMessageReceived Event + */ + public static Event eventSourceMessageReceived() { + return new Event<>(DOMAIN_NAME + ".eventSourceMessageReceived", + map("requestId", EventSourceMessageReceived.class)); + } + + /** + * Fired when HTTP request has failed to load + * + * @return LoadingFailed object + */ + public static Event loadingFailed() { + return new Event<>(DOMAIN_NAME + ".loadingFailed", map("requestId", LoadingFailed.class)); + } + + /** + * Fired when HTTP request has finished loading + * + * @return LoadingFinished object + */ + public static Event loadingFinished() { + return new Event<>(DOMAIN_NAME + ".loadingFinished", map("requestId", LoadingFinished.class)); + } + + /** + * Fired if request ended up loading from cache + * + * @return RequestId object + */ + public static Event requestServedFromCache() { + return new Event<>(DOMAIN_NAME + ".requestServedFromCache", map("requestId", RequestId.class)); + } + + /** + * Fired when resource loading priority is changed (EXPERIMENTAL) + * + * @return ResourceChangedPriority object + */ + @Beta + public static Event resourceChangedPriority() { + return new Event<>(DOMAIN_NAME + ".resourceChangedPriority", + map("requestId", ResourceChangedPriority.class)); + } + + /** + * Fired when a signed exchange was received over the network (EXPERIMENTAL) + * + * @return SignedExchangeReceived object + */ + @Beta + public static Event signedExchangeReceived() { + return new Event<>(DOMAIN_NAME + ".signedExchangeReceived", + map("requestId", SignedExchangeReceived.class)); + } + + /** + * Fired when page is about to send HTTP request + * + * @return RequestWillBeSent object + */ + public static Event requestWillBeSent() { + return new Event<>(DOMAIN_NAME + ".requestWillBeSent", + map("requestId", RequestWillBeSent.class)); + } + + /** + * Details of an intercepted HTTP request, which must be either allowed, blocked, modified or mocked.(EXPERIMENTAL) + * + * @return {@link RequestIntercepted} Object + */ + @Beta + public static Event requestIntercepted() { + return new Event<>(DOMAIN_NAME + ".requestIntercepted", + map("interceptionId", RequestIntercepted.class)); + } + + /** + * Fired when HTTP response is available. + * + * @return {@link ResponseReceived} Object + */ + public static Event responseReceived() { + return new Event<>(DOMAIN_NAME + ".responseReceived", map("requestId", ResponseReceived.class)); + } + + /** + * Fired when WebSocket message error occurs. + */ + public static Event webSocketFrameError() { + return new Event<>(DOMAIN_NAME + ".webSocketFrameError", + map("requestId", WebSocketFrameError.class)); + } + + + /** + * Fired upon WebSocket creation. + */ + public static Event webSocketCreated() { + return new Event<>(DOMAIN_NAME + ".webSocketCreated", map("requestId", WebSocketCreated.class)); + } + + /** + * Fired upon WebSocket creation. + */ + public static Event webSocketClosed() { + return new Event<>(DOMAIN_NAME + ".webSocketClosed", map("requestId", WebSocketClosed.class)); + } + + /** + * Fired when WebSocket message is received. + */ + public static Event webSocketFrameReceived() { + return new Event<>(DOMAIN_NAME + ".webSocketFrameReceived", + map("requestId", WebSocketFrame.class)); + } + + /** + * Fired when WebSocket message is sent. + */ + public static Event webSocketFrameSent() { + return new Event<>(DOMAIN_NAME + ".webSocketFrameSent", map("requestId", WebSocketFrame.class)); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/AuthChallenge.java b/java/client/src/org/openqa/selenium/devtools/network/model/AuthChallenge.java new file mode 100644 index 0000000000000..b65df488a1bfc --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/AuthChallenge.java @@ -0,0 +1,105 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +public class AuthChallenge { + + /** + * Origin of the challenger. + */ + private String origin; + /** + * The realm of the challenge. May be empty. + */ + private String realm; + /** + * The authentication scheme used, such as basic or digest + */ + private String scheme; + /** + * Source of the authentication challenge. + * Optional + */ + private Source source; + + public static AuthChallenge parseRequest(JsonInput input) { + AuthChallenge authChallenge = new AuthChallenge(); + input.beginObject(); + while (input.hasNext()){ + switch (input.nextName()) { + case "origin" : + authChallenge.setOrigin(input.nextString()); + break; + case "realm" : + authChallenge.setRealm(input.nextString()); + break; + case "scheme" : + authChallenge.setScheme(input.nextString()); + break; + case "source" : + authChallenge.setSource(Source.getSource(input.nextString())); + break; + default: + input.skipValue(); + break; + } + } + input.endObject(); + return authChallenge; + } + + + public String getScheme() { + return scheme; + } + + private void setScheme(String scheme) { + requireNonNull(origin, "'scheme' is mandatory for AuthChallenge"); + this.scheme = scheme; + } + + public String getOrigin() { + return origin; + } + + public void setOrigin(String origin) { + requireNonNull(origin, "'origin' is mandatory for AuthChallenge"); + this.origin = origin; + } + + public String getRealm() { + return realm; + } + + private void setRealm(String realm) { + requireNonNull(origin, "'realm' is mandatory for AuthChallenge"); + this.realm = realm; + } + + public Source getSource() { + return source; + } + + public void setSource(Source source) { + this.source = source; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/AuthChallengeResponse.java b/java/client/src/org/openqa/selenium/devtools/network/model/AuthChallengeResponse.java new file mode 100644 index 0000000000000..4496c2a490fb9 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/AuthChallengeResponse.java @@ -0,0 +1,78 @@ +// 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.devtools.network.model; + +import java.util.Objects; + +/** + * Response to an AuthChallenge + */ +public class AuthChallengeResponse { + + /** + * The decision on what to do in response to the authorization challenge. Default means deferring to the default behavior of the net stack, + * which will likely either the Cancel authentication or display a popup dialog box + */ + private final String response; + + /** + * The username to provide, possibly empty. Should only be set if response is ProvideCredentials + */ + private final String username; + + /** + * The password to provide, possibly empty. Should only be set if response is ProvideCredentials + */ + private final String password; + + public AuthChallengeResponse(String response, String username, String password) { + this.response = Objects.requireNonNull(response, "response must be set."); + this.username = username; + this.password = password; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + AuthChallengeResponse that = (AuthChallengeResponse) o; + return Objects.equals(response, that.response) && + Objects.equals(username, that.username) && + Objects.equals(password, that.password); + } + + @Override + public int hashCode() { + + return Objects.hash(response, username, password); + } + + @Override + public String toString() { + return "AuthChallengeResponse{" + + "response='" + response + '\'' + + ", username='" + username + '\'' + + ", password='" + password + '\'' + + '}'; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/BlockedReason.java b/java/client/src/org/openqa/selenium/devtools/network/model/BlockedReason.java new file mode 100644 index 0000000000000..a78214353e896 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/BlockedReason.java @@ -0,0 +1,53 @@ +// 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.devtools.network.model; + +/** + * The reason why request was blocked + */ +public enum BlockedReason { + + other("other"), + csp("csp"), + mixedContent("mixed-content"), + origin("origin"), + inspector("inspector"), + subresourceFilter("subresource-filter"), + contentType("content-type"), + collapsedbyClient("collapsed-by-client"); + + private String reason; + + BlockedReason(String reason) { + this.reason = reason; + } + + public String getReason() { + return reason; + } + + public static BlockedReason fromString(String s) { + for (BlockedReason b : BlockedReason.values()) { + if (b.getReason().equalsIgnoreCase(s)) { + return b; + } + } + return null; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/CallFrame.java b/java/client/src/org/openqa/selenium/devtools/network/model/CallFrame.java new file mode 100644 index 0000000000000..fd376ad95477d --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/CallFrame.java @@ -0,0 +1,151 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInputConverter; +import org.openqa.selenium.json.JsonInput; + +/** + * Stack entry for runtime errors and assertions. + */ +public class CallFrame { + + private String functionName; + + private String scriptId; + + private String url; + + private Integer lineNumber; + + private Integer columnNumber; + + private CallFrame(String functionName, String scriptId, String url, Integer lineNumber, + Integer columnNumber) { + this.functionName = requireNonNull(functionName, "'functionName' is mandatory for CallFrame"); + this.scriptId = requireNonNull(scriptId, "'scriptId' is mandatory for CallFrame"); + this.url = requireNonNull(url, "'url' is mandatory for CallFrame"); + this.lineNumber = requireNonNull(lineNumber, "'lineNumber' is mandatory for CallFrame"); + this.columnNumber = requireNonNull(columnNumber, "'columnNumber' is mandatory for CallFrame"); + } + + /** + * JavaScript function name. + */ + public String getFunctionName() { + return functionName; + } + + /** + * JavaScript function name. + */ + public void setFunctionName(String functionName) { + this.functionName = functionName; + } + + /** + * JavaScript script id. + */ + public String getScriptId() { + return scriptId; + } + + /** + * JavaScript script id. + */ + public void setScriptId(String scriptId) { + this.scriptId = scriptId; + } + + /** + * JavaScript script name or url. + */ + public String getUrl() { + return url; + } + + /** + * JavaScript script name or url. + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * JavaScript script line number (0-based). + */ + public Integer getLineNumber() { + return lineNumber; + } + + /** + * JavaScript script line number (0-based). + */ + public void setLineNumber(Integer lineNumber) { + this.lineNumber = lineNumber; + } + + /** + * JavaScript script column number (0-based). + */ + public Integer getColumnNumber() { + return columnNumber; + } + + /** + * JavaScript script column number (0-based). + */ + public void setColumnNumber(Integer columnNumber) { + this.columnNumber = columnNumber; + } + + public static CallFrame parseCallFrame(JsonInput input) { + String functionName = null; + String scriptId = null; + String callFrameUrl = null; + Integer callFrameLineNumber = null; + Integer columnNumber = null; + input.beginObject(); + while (input.hasNext()) { + switch (input.nextName()) { + case "functionName": + functionName = input.nextString(); + break; + case "scriptId": + scriptId = input.nextString(); + break; + case "url": + callFrameUrl = input.nextString(); + break; + case "lineNumber": + callFrameLineNumber = JsonInputConverter.extractInt(input); + break; + case "columnNumber": + columnNumber = JsonInputConverter.extractInt(input); + break; + default: + input.skipValue(); + break; + } + } + input.endObject(); + return new CallFrame(functionName, scriptId, callFrameUrl, callFrameLineNumber, columnNumber); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/CertificateTransparencyCompliance.java b/java/client/src/org/openqa/selenium/devtools/network/model/CertificateTransparencyCompliance.java new file mode 100644 index 0000000000000..b71fb5d6767dc --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/CertificateTransparencyCompliance.java @@ -0,0 +1,48 @@ +// 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.devtools.network.model; + +/** + * Whether the request complied with Certificate Transparency policy + */ +public enum CertificateTransparencyCompliance { + + Unknown("unknown"), + NotCompliant("not-compliant"), + Compliant("compliant"); + + private String compliance; + + CertificateTransparencyCompliance(String compliance) { + this.compliance = compliance; + } + + public String getCompliance() { + return compliance; + } + + public static CertificateTransparencyCompliance fromString(String s) { + for (CertificateTransparencyCompliance ctp : CertificateTransparencyCompliance.values()) { + if (ctp.getCompliance().equalsIgnoreCase(s)) { + return ctp; + } + } + return null; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/ConnectionType.java b/java/client/src/org/openqa/selenium/devtools/network/model/ConnectionType.java new file mode 100644 index 0000000000000..b698826a8c9d0 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/ConnectionType.java @@ -0,0 +1,35 @@ +// 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.devtools.network.model; + +/** + * The underlying connection technology that the browser is supposedly using + */ +public enum ConnectionType { + + none, + cellular2g, + cellular3g, + cellular4g, + bluetooth, + ethernet, + wifi, + wimax, + other + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/Cookie.java b/java/client/src/org/openqa/selenium/devtools/network/model/Cookie.java new file mode 100644 index 0000000000000..f6b031d3fe96c --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/Cookie.java @@ -0,0 +1,146 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +import java.util.Date; + +/** + * Cookie object + */ +public class Cookie { + + private String name; + + private String value; + + private String domain; + + private String path; + + private long expires; + + private boolean httpOnly; + + private boolean secure; + + public String getName() { + return name; + } + + public String getValue() { + return value; + } + + public String getDomain() { + return domain; + } + + public String getPath() { + return path; + } + + public long getExpires() { + return expires; + } + + public boolean isHttpOnly() { + return httpOnly; + } + + public boolean isSecure() { + return secure; + } + + public Cookie(String name, String value, String domain, String path, long expires, + Boolean httpOnly, Boolean secure) { + this.name = requireNonNull(name, "'name' is required for Cookie"); + this.value = requireNonNull(value, "'value' is required for Cookie"); + this.domain = requireNonNull(domain, "'domain' is required for Cookie"); + this.path = requireNonNull(path, "'path' is required for Cookie"); + this.expires = expires; + this.httpOnly = httpOnly; + this.secure = secure; + } + + org.openqa.selenium.Cookie asSeleniumCookie() { + return new org.openqa.selenium.Cookie.Builder(name, value).domain(domain).path(path) + .expiresOn(new Date(expires)).isSecure(secure).isHttpOnly(httpOnly).build(); + } + + public static Cookie fromSeleniumCookie(org.openqa.selenium.Cookie cookie) { + return new Cookie(cookie.getName(), cookie.getValue(), cookie.getDomain(), cookie.getPath(), + cookie.getExpiry() != null ? cookie.getExpiry().getTime() : 0, + cookie.isHttpOnly(), cookie.isSecure()); + } + + public static Cookie parseCookie(JsonInput input) { + + String name = null; + + String value = null; + + String domain = null; + + String path = null; + + long expires = 0; + + boolean httpOnly = false; + + boolean secure = false; + + input.beginObject(); + while (input.hasNext()) { + switch (input.nextName()) { + case "name": + name = input.nextString(); + break; + case "value": + value = input.nextString(); + break; + case "domain": + domain = input.nextString(); + break; + + case "path": + path = input.nextString(); + break; + + case "expires": + expires = input.nextNumber().longValue(); + break; + case "httpOnly": + httpOnly = input.nextBoolean(); + break; + case "secure": + secure = input.nextBoolean(); + break; + default: + input.skipValue(); + break; + } + } + input.endObject(); + + return new Cookie(name, value, domain, path, expires, httpOnly, secure); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/CookieSameSite.java b/java/client/src/org/openqa/selenium/devtools/network/model/CookieSameSite.java new file mode 100644 index 0000000000000..375574e6a8a84 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/CookieSameSite.java @@ -0,0 +1,30 @@ +// 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.devtools.network.model; + +/** + * Represents the cookie's 'SameSite' status: https://tools.ietf.org/html/draft-west-first-party-cookies + */ +public enum CookieSameSite { + + Strict, + Lax, + Extended, + None + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/Cookies.java b/java/client/src/org/openqa/selenium/devtools/network/model/Cookies.java new file mode 100644 index 0000000000000..67392ef08a17f --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/Cookies.java @@ -0,0 +1,59 @@ +// 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.devtools.network.model; + +import org.openqa.selenium.json.JsonInput; + +import java.util.ArrayList; +import java.util.List; + +/** + * Created by aohana + */ +public class Cookies { + + private List cookies; + + private Cookies(List cookies) { + this.cookies = cookies; + } + + private static Cookies fromJson(JsonInput input) { + + List cookiesList = new ArrayList<>(); + + input.beginArray(); + + while (input.hasNext()) { + cookiesList.add(Cookie.parseCookie(input)); + } + + input.endArray(); + + return new Cookies(cookiesList); + } + + public List asSeleniumCookies() { + List seleniumCookies = new ArrayList<>(); + for (Cookie cookie : cookies) { + seleniumCookies.add(cookie.asSeleniumCookie()); + } + return seleniumCookies; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/DataReceived.java b/java/client/src/org/openqa/selenium/devtools/network/model/DataReceived.java new file mode 100644 index 0000000000000..375480ac674bc --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/DataReceived.java @@ -0,0 +1,111 @@ +// 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.devtools.network.model; + +import org.openqa.selenium.json.JsonInput; + +/** + * Object for storing Network.dataReceived response + */ +public class DataReceived { + + /** + * Request identifier + */ + private final RequestId requestId; + + /** + * MonotonicTime + */ + private final MonotonicTime timestamp; + + /** + * Data chunk length + */ + private final Number dataLength; + + /** + * Actual bytes received (might be less than dataLength for compressed encodings) + */ + private final Number encodedDataLength; + + private DataReceived(RequestId requestId, MonotonicTime timestamp, Number dataLength, + Number encodedDataLength) { + this.requestId = requestId; + this.timestamp = timestamp; + this.dataLength = dataLength; + this.encodedDataLength = encodedDataLength; + } + + private static DataReceived fromJson(JsonInput input) { + RequestId requestId = new RequestId(input.nextString()); + MonotonicTime timestamp = null; + Number dataLength = null; + Number encodedDataLength = null; + + while (input.hasNext()) { + + switch (input.nextName()) { + case "timestamp": + timestamp = MonotonicTime.parse(input.nextNumber()); + break; + + case "dataLength": + dataLength = input.nextNumber(); + break; + + case "encodedDataLength": + encodedDataLength = input.nextNumber(); + break; + + default: + input.skipValue(); + break; + } + } + + return new DataReceived(requestId, timestamp, dataLength, encodedDataLength); + } + + public RequestId getRequestId() { + return requestId; + } + + public MonotonicTime getTimestamp() { + return timestamp; + } + + public Number getDataLength() { + return dataLength; + } + + public Number getEncodedDataLength() { + return encodedDataLength; + } + + @Override + public String toString() { + return "DataReceived{" + + "requestId='" + requestId.toString() + '\'' + + ", timestamp=" + timestamp.getTimeStamp().toString() + + ", dataLength=" + dataLength + + ", encodedDataLength=" + encodedDataLength + + '}'; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/ErrorReason.java b/java/client/src/org/openqa/selenium/devtools/network/model/ErrorReason.java new file mode 100644 index 0000000000000..75d24e57363df --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/ErrorReason.java @@ -0,0 +1,40 @@ +// 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.devtools.network.model; + +/** + * Network level fetch failure reason + */ +public enum ErrorReason { + + Failed, + Aborted, + TimedOut, + AccessDenied, + ConnectionClosed, + ConnectionReset, + ConnectionRefused, + ConnectionAborted, + ConnectionFailed, + NameNotResolved, + InternetDisconnected, + AddressUnreachable, + BlockedByClient, + BlockedByResponse + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/EventSourceMessageReceived.java b/java/client/src/org/openqa/selenium/devtools/network/model/EventSourceMessageReceived.java new file mode 100644 index 0000000000000..46258463b3f6e --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/EventSourceMessageReceived.java @@ -0,0 +1,127 @@ +// 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.devtools.network.model; + +import org.openqa.selenium.json.JsonInput; + +/** + * Object for storing Network.eventSourceMessageReceived response + */ +public class EventSourceMessageReceived { + + /** + * Request identifier + */ + private final String requestId; + + /** + * MonotonicTime + */ + private final MonotonicTime timestamp; + + /** + * Message type + */ + private final String eventName; + + /** + * Message identifier + */ + private final String eventId; + + /** + * Message content + */ + private final String data; + + private EventSourceMessageReceived(String requestId, MonotonicTime timestamp, String eventName, + String eventId, String data) { + this.requestId = requestId; + this.timestamp = timestamp; + this.eventName = eventName; + this.eventId = eventId; + this.data = data; + } + + private static EventSourceMessageReceived fromJson(JsonInput input) { + String requestId = input.nextString(); + MonotonicTime timestamp = null; + String eventName = null; + String eventId = null; + String data = null; + + while (input.hasNext()) { + + switch (input.nextName()) { + case "timestamp": + timestamp = MonotonicTime.parse(input.nextNumber()); + break; + + case "eventName": + eventName = input.nextString(); + break; + + case "eventId": + eventId = input.nextString(); + break; + + case "data": + data = input.nextString(); + break; + + default: + input.skipValue(); + break; + } + } + + return new EventSourceMessageReceived(requestId, timestamp, eventName, eventId, data); + } + + public String getRequestId() { + return requestId; + } + + public MonotonicTime getTimestamp() { + return timestamp; + } + + public String getEventName() { + return eventName; + } + + public String getEventId() { + return eventId; + } + + public String getData() { + return data; + } + + @Override + public String toString() { + return "EventSourceMessageReceived{" + + "requestId='" + requestId + '\'' + + ", timestamp=" + timestamp.getTimeStamp().toString() + + ", eventName='" + eventName + '\'' + + ", eventId='" + eventId + '\'' + + ", data='" + data + '\'' + + '}'; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/Initiator.java b/java/client/src/org/openqa/selenium/devtools/network/model/Initiator.java new file mode 100644 index 0000000000000..7274daa853b7d --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/Initiator.java @@ -0,0 +1,136 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInputConverter; +import org.openqa.selenium.json.JsonInput; + +/** + * Information about the request initiator + */ +public class Initiator { + + private InitiatorType type; + + private StackTrace stack; + + private String url; + + private Double lineNumber; + + private Initiator(InitiatorType type, StackTrace stack, String url, Double lineNumber) { + this.type = requireNonNull(type, "'type' is required for Initiator"); + this.stack = stack; + this.url = url; + this.lineNumber = lineNumber; + } + + /** + * Type of this initiator. + */ + public InitiatorType getType() { + return type; + } + + /** + * Type of this initiator. + */ + public void setType(InitiatorType type) { + this.type = type; + } + + /** + * Initiator JavaScript stack trace, set for Script only. + */ + public StackTrace getStack() { + return stack; + } + + /** + * Initiator JavaScript stack trace, set for Script only. + */ + public void setStack(StackTrace stack) { + this.stack = stack; + } + + /** + * Initiator URL, set for Parser type or for Script type (when script is importing module) or for + * SignedExchange type. + */ + public String getUrl() { + return url; + } + + /** + * Initiator URL, set for Parser type or for Script type (when script is importing module) or for + * SignedExchange type. + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * Initiator line number, set for Parser type or for Script type (when script is importing module) + * (0-based). + */ + public Double getLineNumber() { + return lineNumber; + } + + /** + * Initiator line number, set for Parser type or for Script type (when script is importing module) + * (0-based). + */ + public void setLineNumber(Double lineNumber) { + this.lineNumber = lineNumber; + } + + public static Initiator parseInitiator(JsonInput input) { + + InitiatorType initiatorType = null; + StackTrace stack = null; + String initiatorUrl = null; + Double lineNumber = null; + + input.beginObject(); + while (input.hasNext()) { + switch (input.nextName()) { + case "type": + initiatorType = InitiatorType.valueOf(input.nextString()); + break; + case "stack": + stack = StackTrace.parseStackTrace(input); + break; + case "url": + initiatorUrl = input.nextString(); + break; + case "lineNumber": + lineNumber = JsonInputConverter.extractDouble(input); + break; + default: + input.skipValue(); + break; + } + } + input.endObject(); + return new Initiator(initiatorType, stack, initiatorUrl, lineNumber); + } +} + diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/InitiatorType.java b/java/client/src/org/openqa/selenium/devtools/network/model/InitiatorType.java new file mode 100644 index 0000000000000..413bc24a90cae --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/InitiatorType.java @@ -0,0 +1,29 @@ +// 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.devtools.network.model; + +/** Type of this initiator. */ +public enum InitiatorType { + + parser, + script, + preload, + SignedExchange, + other + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/InterceptionId.java b/java/client/src/org/openqa/selenium/devtools/network/model/InterceptionId.java new file mode 100644 index 0000000000000..cbd7e8a79107c --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/InterceptionId.java @@ -0,0 +1,53 @@ +// 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.devtools.network.model; + +import java.util.Objects; + +/** + * Unique intercepted request identifier + */ +public class InterceptionId { + + private final String interceptionId; + + public InterceptionId(String interceptionId) { + this.interceptionId = Objects.requireNonNull(interceptionId, "InterceptionId must be set."); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof InterceptionId)) { + return false; + } + + InterceptionId that = (InterceptionId) o; + return Objects.equals(interceptionId, that.interceptionId); + } + + @Override + public int hashCode() { + return Objects.hash(interceptionId); + } + + @Override + public String toString() { + return interceptionId; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/InterceptionStage.java b/java/client/src/org/openqa/selenium/devtools/network/model/InterceptionStage.java new file mode 100644 index 0000000000000..aad0981b95e5a --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/InterceptionStage.java @@ -0,0 +1,27 @@ +// 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.devtools.network.model; + +/** + * Stages of the interception to begin intercepting. + * Request will intercept before the request is sent. Response will intercept after the response is received + */ +public enum InterceptionStage { + Request, + HeadersReceived +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/LoaderId.java b/java/client/src/org/openqa/selenium/devtools/network/model/LoaderId.java new file mode 100644 index 0000000000000..bad375237470e --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/LoaderId.java @@ -0,0 +1,53 @@ +// 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.devtools.network.model; + +import java.util.Objects; + +/** + * Unique loader identifier + */ +public class LoaderId { + + private final String loaderId; + + LoaderId(String loaderId) { + this.loaderId = Objects.requireNonNull(loaderId, "LoaderId must be set."); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof LoaderId)) { + return false; + } + + LoaderId that = (LoaderId) o; + return Objects.equals(loaderId, that.loaderId); + } + + @Override + public int hashCode() { + return Objects.hash(loaderId); + } + + @Override + public String toString() { + return loaderId; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/LoadingFailed.java b/java/client/src/org/openqa/selenium/devtools/network/model/LoadingFailed.java new file mode 100644 index 0000000000000..e1213ad82e565 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/LoadingFailed.java @@ -0,0 +1,146 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +/** + * Object for storing Network.loadingFailed response + */ +public class LoadingFailed { + + /** + * Request identifier + */ + private final RequestId requestId; + + /** + * MonotonicTime + */ + private final MonotonicTime timestamp; + + /** + * Resource type + */ + private final ResourceType type; + + /** + * User friendly error message + */ + private final String errorText; + + /** + * True if loading was canceled + */ + private final Boolean canceled; + + /** + * The reason why loading was blocked, if any + */ + private final BlockedReason blockedReason; + + private LoadingFailed(RequestId requestId, MonotonicTime timestamp, + ResourceType resourceType, String errorText, Boolean canceled, + BlockedReason blockedReason) { + this.requestId = requireNonNull(requestId, "'requestId' is required for LoadingFailed"); + this.timestamp = requireNonNull(timestamp, "'timestamp' is required for LoadingFailed"); + this.type = requireNonNull(resourceType, "'resourceType' is required for LoadingFailed"); + this.errorText = requireNonNull(errorText, "'errorText' is required for LoadingFailed"); + this.canceled = canceled; + this.blockedReason = blockedReason; + } + + private static LoadingFailed fromJson(JsonInput input) { + RequestId requestId = new RequestId(input.nextString()); + MonotonicTime timestamp = null; + ResourceType type = null; + String errorText = null; + Boolean canceled = null; + BlockedReason blockedReason = null; + + while (input.hasNext()) { + + switch (input.nextName()) { + case "timestamp": + timestamp = MonotonicTime.parse(input.nextNumber()); + break; + + case "type": + type = ResourceType.valueOf(input.nextString()); + break; + + case "errorText": + errorText = input.nextString(); + break; + + case "canceled": + canceled = input.nextBoolean(); + break; + + case "blockedReason": + blockedReason = BlockedReason.fromString(input.nextString()); + break; + + default: + input.skipValue(); + break; + } + } + + return new LoadingFailed(requestId, timestamp, type, errorText, canceled, blockedReason); + } + + public RequestId getRequestId() { + return requestId; + } + + public MonotonicTime getTimestamp() { + return timestamp; + } + + public ResourceType getResourceType() { + return type; + } + + public String getErrorText() { + return errorText; + } + + public Boolean getCanceled() { + return canceled; + } + + public BlockedReason getBlockedReason() { + return blockedReason; + } + + @Override + public String toString() { + return "LoadingFailed{" + + "requestId=" + requestId + + ", timestamp=" + timestamp.getTimeStamp().toString() + + ", resourceType=" + type + + ", errorText='" + errorText + '\'' + + ", canceled=" + canceled + + ", blockedReason=" + blockedReason + + '}'; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/LoadingFinished.java b/java/client/src/org/openqa/selenium/devtools/network/model/LoadingFinished.java new file mode 100644 index 0000000000000..4225a84861a9e --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/LoadingFinished.java @@ -0,0 +1,114 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +/** + * Object for storing Network.loadingFinished response + */ +public class LoadingFinished { + + /** + * Request identifier + */ + private final RequestId requestId; + + /** + * MonotonicTime + */ + private final MonotonicTime timestamp; + + /** + * Total number of bytes received for this request + */ + private final Number encodedDataLength; + + /** + * Set when 1) response was blocked by Cross-Origin Read Blocking and also 2) this needs to be reported to the DevTools console + */ + private final Boolean shouldReportCorbBlocking; + + private LoadingFinished(RequestId requestId, MonotonicTime timestamp, Number encodedDataLength, + Boolean shouldReportCorbBlocking) { + this.requestId = requireNonNull(requestId, "'requestId' is required for LoadingFinished"); + this.timestamp = requireNonNull(timestamp, "'timestamp' is required for LoadingFinished"); + this.encodedDataLength = + requireNonNull(encodedDataLength, "'encodedDataLength' is required for LoadingFinished"); + this.shouldReportCorbBlocking = shouldReportCorbBlocking; + } + + private static LoadingFinished fromJson(JsonInput input) { + RequestId requestId = new RequestId(input.nextString()); + MonotonicTime timestamp = null; + Number encodedDataLength = null; + Boolean shouldReportCorbBlocking = null; + + while (input.hasNext()) { + + switch (input.nextName()) { + case "timestamp": + timestamp = MonotonicTime.parse(input.nextNumber()); + break; + + case "encodedDataLength": + encodedDataLength = input.nextNumber(); + break; + + case "shouldReportCorbBlocking": + shouldReportCorbBlocking = input.nextBoolean(); + break; + + default: + input.skipValue(); + break; + } + } + + return new LoadingFinished(requestId, timestamp, encodedDataLength, shouldReportCorbBlocking); + } + + public RequestId getRequestId() { + return requestId; + } + + public MonotonicTime getTimestamp() { + return timestamp; + } + + public Number getEncodedDataLength() { + return encodedDataLength; + } + + public Boolean getShouldReportCorbBlocking() { + return shouldReportCorbBlocking; + } + + @Override + public String toString() { + return "LoadingFinished{" + + "requestId=" + requestId + + ", timestamp=" + timestamp.getTimeStamp().toString() + + ", encodedDataLength=" + encodedDataLength + + ", shouldReportCorbBlocking=" + shouldReportCorbBlocking + + '}'; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/MixedContentType.java b/java/client/src/org/openqa/selenium/devtools/network/model/MixedContentType.java new file mode 100644 index 0000000000000..6576250a2bf51 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/MixedContentType.java @@ -0,0 +1,49 @@ +// 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.devtools.network.model; + +/** + * A description of mixed content (HTTP resources on HTTPS pages), as defined by + * https://www.w3.org/TR/mixed-content/#categories + */ +public enum MixedContentType { + + blockable("blockable"), + optionallyBlockable("optionally-blockable"), + none("none"); + + private String type; + + MixedContentType(String type) { + this.type = type; + } + + public String getType() { + return type; + } + + public static MixedContentType fromString(String s) { + for (MixedContentType m : MixedContentType.values()) { + if (m.getType().equalsIgnoreCase(s)) { + return m; + } + } + return null; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/MonotonicTime.java b/java/client/src/org/openqa/selenium/devtools/network/model/MonotonicTime.java new file mode 100644 index 0000000000000..dee2136673ca1 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/MonotonicTime.java @@ -0,0 +1,41 @@ +// 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.devtools.network.model; + +import java.time.Instant; +import java.util.Objects; + +public class MonotonicTime { + + private Instant timestamp; + + public static MonotonicTime parse(Number nextNumber) { + MonotonicTime monotonicTime = new MonotonicTime(); + monotonicTime.setTimeStamp(nextNumber); + return monotonicTime; + } + + public Instant getTimeStamp() { + return timestamp; + } + + private void setTimeStamp(Number timeStamp) { + Objects.requireNonNull(timeStamp,"'timestamp' is require for MonotonicTime"); + this.timestamp = Instant.ofEpochSecond(timeStamp.longValue()); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/Request.java b/java/client/src/org/openqa/selenium/devtools/network/model/Request.java new file mode 100644 index 0000000000000..e45304c18b9c4 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/Request.java @@ -0,0 +1,267 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; +import org.openqa.selenium.json.JsonInputConverter; + +import java.util.Map; + +/** + * HTTP request data + */ +public class Request { + + private String url; + + private String urlFragment; + + private String method; + + private Map headers; + + private String postData; + + private Boolean hasPostData; + + private MixedContentType mixedContentType; + + private ResourcePriority initialPriority; + + private RequestReferrerPolicy referrerPolicy; + + private Boolean isLinkPreload; + + public Request() { + } + + public Request(String url, String urlFragment, String method, + Map headers, String postData, Boolean hasPostData, + MixedContentType mixedContentType, + ResourcePriority initialPriority, + RequestReferrerPolicy referrerPolicy, Boolean isLinkPreload) { + this.url = requireNonNull(url, "'url' is require"); + this.urlFragment = urlFragment; + this.method = method; + this.headers = headers; + this.postData = postData; + this.hasPostData = hasPostData; + this.mixedContentType = mixedContentType; + this.initialPriority = initialPriority; + this.referrerPolicy = referrerPolicy; + this.isLinkPreload = isLinkPreload; + } + + /** + * Request URL (without fragment). + */ + public String getUrl() { + return url; + } + + /** + * Request URL (without fragment). + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * Fragment of the requested URL starting with hash, if present. + */ + public String getUrlFragment() { + return urlFragment; + } + + /** + * Fragment of the requested URL starting with hash, if present. + */ + public void setUrlFragment(String urlFragment) { + this.urlFragment = urlFragment; + } + + /** + * HTTP request method. + */ + public String getMethod() { + return method; + } + + /** + * HTTP request method. + */ + public void setMethod(String method) { + this.method = method; + } + + /** + * HTTP request headers. + */ + public Map getHeaders() { + return headers; + } + + /** + * HTTP request headers. + */ + public void setHeaders(Map headers) { + this.headers = headers; + } + + /** + * HTTP POST request data. + */ + public String getPostData() { + return postData; + } + + /** + * HTTP POST request data. + */ + public void setPostData(String postData) { + this.postData = postData; + } + + /** + * True when the request has POST data. Note that postData might still be omitted when this flag + * is true when the data is too long. + */ + public Boolean getHasPostData() { + return hasPostData; + } + + /** + * True when the request has POST data. Note that postData might still be omitted when this flag + * is true when the data is too long. + */ + public void setHasPostData(Boolean hasPostData) { + this.hasPostData = hasPostData; + } + + /** + * The mixed content type of the request. + */ + public MixedContentType getMixedContentType() { + return mixedContentType; + } + + /** + * The mixed content type of the request. + */ + public void setMixedContentType(MixedContentType mixedContentType) { + this.mixedContentType = mixedContentType; + } + + /** + * Priority of the resource request at the time request is sent. + */ + public ResourcePriority getInitialPriority() { + return initialPriority; + } + + /** + * Priority of the resource request at the time request is sent. + */ + public void setInitialPriority(ResourcePriority initialPriority) { + this.initialPriority = initialPriority; + } + + /** + * The referrer policy of the request, as defined in https://www.w3.org/TR/referrer-policy/ + */ + public RequestReferrerPolicy getReferrerPolicy() { + return referrerPolicy; + } + + /** + * The referrer policy of the request, as defined in https://www.w3.org/TR/referrer-policy/ + */ + public void setReferrerPolicy(RequestReferrerPolicy referrerPolicy) { + this.referrerPolicy = referrerPolicy; + } + + /** + * Whether is loaded via link preload. + */ + public Boolean getIsLinkPreload() { + return isLinkPreload; + } + + /** + * Whether is loaded via link preload. + */ + public void setIsLinkPreload(Boolean isLinkPreload) { + this.isLinkPreload = isLinkPreload; + } + + public static Request parseRequest(JsonInput input) { + input.beginObject(); + String url = null; + String method = null; + String urlFragment = null; + Map headers = null; + String postData = null; + Boolean hasPostData = null; + MixedContentType mixedContentType = null; + ResourcePriority initialPriority = null; + RequestReferrerPolicy referrerPolicy = null; + Boolean isLinkPreload = null; + + while (input.hasNext()) { + switch (input.nextName()) { + case "url": + url = input.nextString(); + break; + case "method": + method = input.nextString(); + break; + case "urlFragment": + urlFragment = input.nextString(); + break; + + case "headers": + headers = JsonInputConverter.extractMap(input); + break; + + case "postData": + postData = input.nextString(); + break; + case "mixedContentType": + mixedContentType = MixedContentType.fromString(input.nextString()); + break; + case "initialPriority": + initialPriority = ResourcePriority.valueOf(input.nextString()); + break; + case "referrerPolicy": + referrerPolicy = RequestReferrerPolicy.fromString(input.nextString()); + break; + case "isLinkPreload": + isLinkPreload = input.nextBoolean(); + break; + default: + input.skipValue(); + break; + } + } + input.endObject(); + return new Request(url, urlFragment, method, headers, postData, hasPostData, + mixedContentType, initialPriority, referrerPolicy, isLinkPreload); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/RequestId.java b/java/client/src/org/openqa/selenium/devtools/network/model/RequestId.java new file mode 100644 index 0000000000000..508e01503efaa --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/RequestId.java @@ -0,0 +1,57 @@ +// 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.devtools.network.model; + +import java.util.Objects; + +/** + * Unique request identifier + */ +public class RequestId { + + private String requestId; + + public RequestId(String requestId) { + this.requestId = Objects.requireNonNull(requestId, "RequestId must be set."); + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof RequestId)) { + return false; + } + + RequestId that = (RequestId) o; + return Objects.equals(requestId, that.requestId); + } + + @Override + public int hashCode() { + return Objects.hash(requestId); + } + + @Override + public String toString() { + return requestId; + } + + private static RequestId fromJson(String requestId) { + return new RequestId(requestId); + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/RequestIntercepted.java b/java/client/src/org/openqa/selenium/devtools/network/model/RequestIntercepted.java new file mode 100644 index 0000000000000..6b9b77ac81470 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/RequestIntercepted.java @@ -0,0 +1,230 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; +import org.openqa.selenium.json.JsonInputConverter; + +public class RequestIntercepted { + + /** + * Each request the page makes will have a unique id, however if any redirects are encountered while processing that fetch, they will be reported with the same id as the original fetch. Likewise if HTTP authentication is needed then the same fetch id will be used + */ + private final InterceptionId interceptionId; + /** + * Request data + */ + private final Request request; + /** + * The id of the frame that initiated the request. + */ + private final String frameId; + /** + * How the requested resource will be used. + */ + private final ResourceType resourceType; + + /** + * Whether this is a navigation request, which can abort the navigation completely. + */ + private final boolean isNavigationRequest; + + /** + * Set if the request is a navigation that will result in a download. Only present after response is received from the server (i.e. HeadersReceived stage). + * Optional + */ + private final Boolean isDownload; + /** + * Redirect location, only sent if a redirect was intercepted. + * Optional + */ + private final String redirectUrl; + /** + * Details of the Authorization Challenge encountered. If this is set then continueInterceptedRequest must contain an authChallengeResponse. + * Optional + */ + private final AuthChallenge authChallenge; + /** + * Response error if intercepted at response stage or if redirect occurred while intercepting request. + * Optional + */ + private final ErrorReason responseErrorReason; + + /** + * Response code if intercepted at response stage or if redirect occurred while intercepting request or auth retry occurred. + * Optional + */ + private final Number responseStatusCode; + + /** + * Response headers if intercepted at the response stage or if redirect occurred while intercepting request or auth retry occurred. + * Optional + */ + private final Object responseHeaders; + /** + * If the intercepted request had a corresponding requestWillBeSent event fired for it, then this requestId will be the same as the requestId present in the requestWillBeSent event. + * Optional + */ + private final String requestId; + + + private RequestIntercepted(InterceptionId interceptionId, + Request request, + String frameId, + ResourceType resourceType, + boolean isNavigationRequest, + Boolean isDownload, + String redirectUrl, + AuthChallenge authChallenge, + ErrorReason responseErrorReason, + Number responseStatusCode, + Object responseHeaders, + String requestId) { + this.interceptionId = + requireNonNull(interceptionId, "'interceptionId' is required for RequestIntercepted"); + this.request = requireNonNull(request, "'request' is required for RequestIntercepted"); + this.frameId = requireNonNull(frameId, "'frameId' is required for RequestIntercepted"); + this.resourceType = + requireNonNull(resourceType, "'resourceType' is required for RequestIntercepted"); + this.isNavigationRequest = isNavigationRequest; + this.isDownload = isDownload; + this.redirectUrl = redirectUrl; + this.authChallenge = authChallenge; + this.responseErrorReason = responseErrorReason; + this.responseStatusCode = responseStatusCode; + this.responseHeaders = responseHeaders; + this.requestId = requestId; + } + + public static RequestIntercepted fromJson(JsonInput input) { + InterceptionId interceptionId = new InterceptionId(input.nextString()); + Request request = null; + String frameId = null; + ResourceType resourceType = null; + Boolean isNavigationRequest = null; + Boolean isDownload = null; + String redirectUrl = null; + AuthChallenge authChallenge = null; + ErrorReason responseErrorReason = null; + Number responseStatusCode = null; + Object responseHeaders = null; + String requestId = null; + while (input.hasNext()) { + switch (input.nextName()) { + case "request": + request = Request.parseRequest(input); + break; + case "frameId": + frameId = input.nextString(); + break; + case "resourceType": + resourceType = ResourceType.valueOf(input.nextString()); + break; + case "isNavigationRequest": + isNavigationRequest = input.nextBoolean(); + break; + case "isDownload": + isDownload = input.nextBoolean(); + break; + case "redirectUrl": + redirectUrl = input.nextString(); + break; + case "authChallenge": + authChallenge = AuthChallenge.parseRequest(input); + break; + case "responseErrorReason": + responseErrorReason = ErrorReason.valueOf(input.nextString()); + break; + case "responseStatusCode": + responseStatusCode = input.nextNumber(); + break; + case "responseHeaders": + responseHeaders = JsonInputConverter.extractMap(input); + break; + case "requestId": + requestId = input.nextString(); + break; + default: + input.skipValue(); + break; + } + } + return new RequestIntercepted(interceptionId, + request, + frameId, + resourceType, + isNavigationRequest, + isDownload, + redirectUrl, + authChallenge, + responseErrorReason, + responseStatusCode, + responseHeaders, + requestId); + } + + public InterceptionId getInterceptionId() { + return interceptionId; + } + + public Request getRequest() { + return request; + } + + public String getFrameId() { + return frameId; + } + + public ResourceType getResourceType() { + return resourceType; + } + + public boolean isNavigationRequest() { + return isNavigationRequest; + } + + public Boolean getDownload() { + return isDownload; + } + + public String getRedirectUrl() { + return redirectUrl; + } + + public AuthChallenge getAuthChallenge() { + return authChallenge; + } + + public ErrorReason getResponseErrorReason() { + return responseErrorReason; + } + + public Number getResponseStatusCode() { + return responseStatusCode; + } + + public Object getResponseHeaders() { + return responseHeaders; + } + + public String getRequestId() { + return requestId; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/RequestPattern.java b/java/client/src/org/openqa/selenium/devtools/network/model/RequestPattern.java new file mode 100644 index 0000000000000..a7ac43368bb41 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/RequestPattern.java @@ -0,0 +1,82 @@ +// 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.devtools.network.model; + +/** + * Request pattern for interception + */ +public class RequestPattern { + + private String urlPattern; + + private ResourceType resourceType; + + private InterceptionStage interceptionStage; + + public RequestPattern(String urlPattern, + ResourceType resourceType, + InterceptionStage interceptionStage) { + this.urlPattern = urlPattern; + this.resourceType = resourceType; + this.interceptionStage = interceptionStage; + } + + /** + * Wildcards ('*' -> zero or more, '?' -> exactly one) are allowed. Escape character is backslash. + * Omitting is equivalent to "*". + */ + public String getUrlPattern() { + return urlPattern; + } + + /** + * Wildcards ('*' -> zero or more, '?' -> exactly one) are allowed. Escape character is backslash. + * Omitting is equivalent to "*". + */ + public void setUrlPattern(String urlPattern) { + this.urlPattern = urlPattern; + } + + /** + * If set, only requests for matching resource types will be intercepted. + */ + public ResourceType getResourceType() { + return resourceType; + } + + /** + * If set, only requests for matching resource types will be intercepted. + */ + public void setResourceType(ResourceType resourceType) { + this.resourceType = resourceType; + } + + /** + * Stage at which to begin intercepting requests. Default is Request. + */ + public InterceptionStage getInterceptionStage() { + return interceptionStage; + } + + /** + * Stage at which to begin intercepting requests. Default is Request. + */ + public void setInterceptionStage(InterceptionStage interceptionStage) { + this.interceptionStage = interceptionStage; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/RequestReferrerPolicy.java b/java/client/src/org/openqa/selenium/devtools/network/model/RequestReferrerPolicy.java new file mode 100644 index 0000000000000..3a7342612a5bb --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/RequestReferrerPolicy.java @@ -0,0 +1,53 @@ +// 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.devtools.network.model; + +/** + * The referrer policy of the request, as defined in https://www.w3.org/TR/referrer-policy/ + */ +public enum RequestReferrerPolicy { + + unsafeUrl("unsafe-url"), + noReferrerWhenDowngrade("no-referrer-when-downgrade"), + noReferrer("no-referrer"), + origin("origin"), + originWhenCrossOrigin("origin-when-cross-origin"), + sameOrigin("same-origin"), + strictOrigin("strict-origin"), + strictOriginWhenCrossOrigin("strict-origin-when-cross-origin"); + + private String policy; + + RequestReferrerPolicy(String policy) { + this.policy = policy; + } + + public String getPolicy() { + return policy; + } + + public static RequestReferrerPolicy fromString(String s) { + for (RequestReferrerPolicy r : RequestReferrerPolicy.values()) { + if (r.getPolicy().equalsIgnoreCase(s)) { + return r; + } + } + return null; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/RequestWillBeSent.java b/java/client/src/org/openqa/selenium/devtools/network/model/RequestWillBeSent.java new file mode 100644 index 0000000000000..b703b43c71905 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/RequestWillBeSent.java @@ -0,0 +1,235 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +/** + * Object for storing Network.requestWillBeSent response + */ +public class RequestWillBeSent { + + /** + * Request identifier + */ + private final RequestId requestId; + + /** + * Loader identifier. Empty string if the request is fetched from worker + */ + private final LoaderId loaderId; + + /** + * URL of the document this request is loaded for + */ + private final String documentURL; + + /** + * Request data + */ + private final Request request; + + /** + * MonotonicTime + */ + private final MonotonicTime timestamp; + + /** + * MonotonicTime + */ + private final Number wallTime; + + + /** + * Request initiator + */ + private final Initiator initiator; + + /** + * Redirect response data + */ + private final Response redirectResponse; + + /** + * Type of this resource + */ + private final ResourceType type; + + + /** + * Frame identifier + */ + private final String frameId; + + /** + * Whether the request is initiated by a user gesture. Defaults to false + */ + private final Boolean hasUserGesture; + + private RequestWillBeSent(RequestId requestId, + LoaderId loaderId, String documentURL, + Request request, MonotonicTime timestamp, Number wallTime, + Initiator initiator, + Response redirectResponse, + ResourceType type, String frameId, Boolean hasUserGesture) { + this.requestId = requireNonNull(requestId, "'requestId' is required for RequestWillBeSent"); + this.loaderId = requireNonNull(loaderId, "'loaderId' is required for RequestWillBeSent"); + this.documentURL = + requireNonNull(documentURL, "'documentURL' is required for RequestWillBeSent"); + this.request = requireNonNull(request, "'request' is required for RequestWillBeSent"); + this.timestamp = requireNonNull(timestamp, "'timestamp' is required for RequestWillBeSent"); + this.wallTime = requireNonNull(wallTime, "'wallTime' is required for RequestWillBeSent"); + this.initiator = requireNonNull(initiator, "'initiator' is required for RequestWillBeSent"); + this.redirectResponse = redirectResponse; + this.type = type; + this.frameId = frameId; + this.hasUserGesture = hasUserGesture; + } + + private static RequestWillBeSent fromJson(JsonInput input) { + + RequestId requestId = new RequestId(input.nextString()); + LoaderId loaderId = null; + String documentURL = null; + Request request = null; + MonotonicTime timestamp = null; + Number wallTime = null; + Initiator initiator = null; + Response redirectResponse = null; + ResourceType type = null; + String frameId = null; + Boolean hasUserGesture = null; + + while (input.hasNext()) { + + switch (input.nextName()) { + + case "loaderId": + loaderId = new LoaderId(input.nextString()); + break; + + case "documentURL": + documentURL = input.nextString(); + break; + + case "request": + request = Request.parseRequest(input); + break; + + case "timestamp": + timestamp = MonotonicTime.parse(input.nextNumber()); + break; + + case "wallTime": + wallTime = input.nextNumber(); + break; + + case "initiator": + initiator = Initiator.parseInitiator(input); + break; + + case "redirectResponse": + redirectResponse = Response.parseResponse(input); + break; + + case "type": + type = ResourceType.valueOf(input.nextString()); + break; + + case "frameId": + frameId = input.nextString(); + break; + + case "hasUserGesture": + hasUserGesture = input.nextBoolean(); + break; + + default: + input.skipValue(); + break; + } + } + + return new RequestWillBeSent(requestId, loaderId, documentURL, request, timestamp, wallTime, + initiator, redirectResponse, type, frameId, hasUserGesture); + } + + public RequestId getRequestId() { + return requestId; + } + + public LoaderId getLoaderId() { + return loaderId; + } + + public String getDocumentURL() { + return documentURL; + } + + public Request getRequest() { + return request; + } + + public MonotonicTime getTimestamp() { + return timestamp; + } + + public Number getWallTime() { + return wallTime; + } + + public Initiator getInitiator() { + return initiator; + } + + public Response getRedirectResponse() { + return redirectResponse; + } + + public ResourceType getType() { + return type; + } + + public String getFrameId() { + return frameId; + } + + public Boolean getHasUserGesture() { + return hasUserGesture; + } + + @Override + public String toString() { + return "RequestWillBeSent{" + + "requestId=" + requestId + + ", loaderId=" + loaderId + + ", documentURL='" + documentURL + '\'' + + ", request=" + request + + ", timestamp=" + timestamp.getTimeStamp().toString() + + ", wallTime=" + wallTime + + ", initiator=" + initiator + + ", redirectResponse=" + redirectResponse + + ", type=" + type + + ", frameId='" + frameId + '\'' + + ", hasUserGesture=" + hasUserGesture + + '}'; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/ResourceChangedPriority.java b/java/client/src/org/openqa/selenium/devtools/network/model/ResourceChangedPriority.java new file mode 100644 index 0000000000000..4d1d451bba76d --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/ResourceChangedPriority.java @@ -0,0 +1,95 @@ +// 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.devtools.network.model; + +import org.openqa.selenium.json.JsonInput; + +/** + * Object for storing Network.resourceChangedPriority response + */ +public class ResourceChangedPriority { + + /** + * Request identifier + */ + private final RequestId requestId; + + /** + * Total number of bytes received for this request + */ + private final ResourcePriority newPriority; + + /** + * MonotonicTime + */ + private final MonotonicTime timestamp; + + private ResourceChangedPriority(RequestId requestId, + ResourcePriority newPriority, MonotonicTime timestamp) { + this.requestId = requestId; + this.newPriority = newPriority; + this.timestamp = timestamp; + } + + private static ResourceChangedPriority fromJson(JsonInput input) { + RequestId requestId = new RequestId(input.nextString()); + ResourcePriority newPriority = null; + MonotonicTime timestamp = null; + + while (input.hasNext()) { + + switch (input.nextName()) { + case "newPriority": + newPriority = ResourcePriority.valueOf(input.nextString()); + break; + + case "timestamp": + timestamp = MonotonicTime.parse(input.nextNumber()); + break; + + default: + input.skipValue(); + break; + } + } + + return new ResourceChangedPriority(requestId, newPriority, timestamp); + } + + public RequestId getRequestId() { + return requestId; + } + + public ResourcePriority getNewPriority() { + return newPriority; + } + + public MonotonicTime getTimestamp() { + return timestamp; + } + + @Override + public String toString() { + return "ResourceChangedPriority{" + + "requestId=" + requestId + + ", newPriority=" + newPriority + + ", timestamp=" + timestamp.getTimeStamp().toString() + + '}'; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/ResourcePriority.java b/java/client/src/org/openqa/selenium/devtools/network/model/ResourcePriority.java new file mode 100644 index 0000000000000..80f7db978bc10 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/ResourcePriority.java @@ -0,0 +1,31 @@ +// 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.devtools.network.model; + +/** + * Loading priority of a resource request + */ +public enum ResourcePriority { + + VeryLow, + Low, + Medium, + High, + VeryHigh + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/ResourceTiming.java b/java/client/src/org/openqa/selenium/devtools/network/model/ResourceTiming.java new file mode 100644 index 0000000000000..a1800fc5e668e --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/ResourceTiming.java @@ -0,0 +1,222 @@ +// 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.devtools.network.model; + +/** + * Timing information for the request + */ +public class ResourceTiming { + + private Double requestTime; + + private Double proxyStart; + + private Double proxyEnd; + + private Double dnsStart; + + private Double dnsEnd; + + private Double connectStart; + + private Double connectEnd; + + private Double sslStart; + + private Double sslEnd; + + private Double workerStart; + + private Double workerReady; + + private Double sendStart; + + private Double sendEnd; + + private Double pushStart; + + private Double pushEnd; + + private Double receiveHeadersEnd; + + /** + * Timing's requestTime is a baseline in seconds, while the other numbers are ticks in + * milliseconds relatively to this requestTime. + */ + public Double getRequestTime() { + return requestTime; + } + + /** + * Timing's requestTime is a baseline in seconds, while the other numbers are ticks in + * milliseconds relatively to this requestTime. + */ + public void setRequestTime(Double requestTime) { + this.requestTime = requestTime; + } + + /** Started resolving proxy. */ + public Double getProxyStart() { + return proxyStart; + } + + /** Started resolving proxy. */ + public void setProxyStart(Double proxyStart) { + this.proxyStart = proxyStart; + } + + /** Finished resolving proxy. */ + public Double getProxyEnd() { + return proxyEnd; + } + + /** Finished resolving proxy. */ + public void setProxyEnd(Double proxyEnd) { + this.proxyEnd = proxyEnd; + } + + /** Started DNS address resolve. */ + public Double getDnsStart() { + return dnsStart; + } + + /** Started DNS address resolve. */ + public void setDnsStart(Double dnsStart) { + this.dnsStart = dnsStart; + } + + /** Finished DNS address resolve. */ + public Double getDnsEnd() { + return dnsEnd; + } + + /** Finished DNS address resolve. */ + public void setDnsEnd(Double dnsEnd) { + this.dnsEnd = dnsEnd; + } + + /** Started connecting to the remote host. */ + public Double getConnectStart() { + return connectStart; + } + + /** Started connecting to the remote host. */ + public void setConnectStart(Double connectStart) { + this.connectStart = connectStart; + } + + /** Connected to the remote host. */ + public Double getConnectEnd() { + return connectEnd; + } + + /** Connected to the remote host. */ + public void setConnectEnd(Double connectEnd) { + this.connectEnd = connectEnd; + } + + /** Started SSL handshake. */ + public Double getSslStart() { + return sslStart; + } + + /** Started SSL handshake. */ + public void setSslStart(Double sslStart) { + this.sslStart = sslStart; + } + + /** Finished SSL handshake. */ + public Double getSslEnd() { + return sslEnd; + } + + /** Finished SSL handshake. */ + public void setSslEnd(Double sslEnd) { + this.sslEnd = sslEnd; + } + + /** Started running ServiceWorker. */ + public Double getWorkerStart() { + return workerStart; + } + + /** Started running ServiceWorker. */ + public void setWorkerStart(Double workerStart) { + this.workerStart = workerStart; + } + + /** Finished Starting ServiceWorker. */ + public Double getWorkerReady() { + return workerReady; + } + + /** Finished Starting ServiceWorker. */ + public void setWorkerReady(Double workerReady) { + this.workerReady = workerReady; + } + + /** Started sending request. */ + public Double getSendStart() { + return sendStart; + } + + /** Started sending request. */ + public void setSendStart(Double sendStart) { + this.sendStart = sendStart; + } + + /** Finished sending request. */ + public Double getSendEnd() { + return sendEnd; + } + + /** Finished sending request. */ + public void setSendEnd(Double sendEnd) { + this.sendEnd = sendEnd; + } + + /** Time the server started pushing request. */ + public Double getPushStart() { + return pushStart; + } + + /** Time the server started pushing request. */ + public void setPushStart(Double pushStart) { + this.pushStart = pushStart; + } + + /** Time the server finished pushing request. */ + public Double getPushEnd() { + return pushEnd; + } + + /** Time the server finished pushing request. */ + public void setPushEnd(Double pushEnd) { + this.pushEnd = pushEnd; + } + + /** Finished receiving response headers. */ + public Double getReceiveHeadersEnd() { + return receiveHeadersEnd; + } + + /** Finished receiving response headers. */ + public void setReceiveHeadersEnd(Double receiveHeadersEnd) { + this.receiveHeadersEnd = receiveHeadersEnd; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/ResourceType.java b/java/client/src/org/openqa/selenium/devtools/network/model/ResourceType.java new file mode 100644 index 0000000000000..1a7b64cd58c55 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/ResourceType.java @@ -0,0 +1,42 @@ +// 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.devtools.network.model; + +/** + * Resource type as it was perceived by the rendering engine + */ +public enum ResourceType { + + Document, + Stylesheet, + Image, + Media, + Font, + Script, + TextTrack, + XHR, + Fetch, + EventSource, + WebSocket, + Manifest, + SignedExchange, + Ping, + CSPViolationReport, + Other + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/Response.java b/java/client/src/org/openqa/selenium/devtools/network/model/Response.java new file mode 100644 index 0000000000000..326e86c4baf6c --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/Response.java @@ -0,0 +1,486 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; +import org.openqa.selenium.json.JsonInputConverter; + +import java.util.Map; + +/** + * HTTP response data + */ +public class Response { + + private String url; + + private Integer status; + + private String statusText; + + private Map headers; + + private String headersText; + + private String mimeType; + + private Map requestHeaders; + + private String requestHeadersText; + + private Boolean connectionReused; + + private Double connectionId; + + private String remoteIPAddress; + + private Integer remotePort; + + private Boolean fromDiskCache; + + private Boolean fromServiceWorker; + + private Double encodedDataLength; + + private ResourceTiming timing; + + private String protocol; + + private SecurityState securityState; + + private SecurityDetails securityDetails; + + public Response() { + } + + public Response(String url, Integer status, String statusText, + Map headers, String headersText, String mimeType, + Map requestHeaders, String requestHeadersText, + Boolean connectionReused, Double connectionId, String remoteIPAddress, + Integer remotePort, Boolean fromDiskCache, Boolean fromServiceWorker, + Double encodedDataLength, + ResourceTiming timing, String protocol, + SecurityState securityState, + SecurityDetails securityDetails) { + this.url = requireNonNull(url, "'url' is required for Response"); + this.status = requireNonNull(status, "'status' is required for Response"); + this.statusText = requireNonNull(statusText, "'statusText' is required for Response"); + this.headers = requireNonNull(headers, "'headers' is required for Response"); + this.headersText = headersText; + this.mimeType = requireNonNull(mimeType, "'mimeType' is required for Response"); + this.requestHeaders = requestHeaders; + this.requestHeadersText = requestHeadersText; + this.connectionReused = + requireNonNull(connectionReused, "'connectionReused' is required for Response"); + this.connectionId = requireNonNull(connectionId, "'connectionId' is required for Response"); + this.remoteIPAddress = remoteIPAddress; + this.remotePort = remotePort; + this.fromDiskCache = fromDiskCache; + this.fromServiceWorker = fromServiceWorker; + this.encodedDataLength = + requireNonNull(encodedDataLength, "'encodedDataLength' is required for Response"); + this.timing = timing; + this.protocol = protocol; + this.securityState = securityState; + this.securityDetails = securityDetails; + } + + /** + * Response URL. This URL can be different from CachedResource.url in case of redirect. + */ + public String getUrl() { + return url; + } + + /** + * Response URL. This URL can be different from CachedResource.url in case of redirect. + */ + public void setUrl(String url) { + this.url = url; + } + + /** + * HTTP response status code. + */ + public Integer getStatus() { + return status; + } + + /** + * HTTP response status code. + */ + public void setStatus(Integer status) { + this.status = status; + } + + /** + * HTTP response status text. + */ + public String getStatusText() { + return statusText; + } + + /** + * HTTP response status text. + */ + public void setStatusText(String statusText) { + this.statusText = statusText; + } + + /** + * HTTP response headers. + */ + public Map getHeaders() { + return headers; + } + + /** + * HTTP response headers. + */ + public void setHeaders(Map headers) { + this.headers = headers; + } + + /** + * HTTP response headers text. + */ + public String getHeadersText() { + return headersText; + } + + /** + * HTTP response headers text. + */ + public void setHeadersText(String headersText) { + this.headersText = headersText; + } + + /** + * Resource mimeType as determined by the browser. + */ + public String getMimeType() { + return mimeType; + } + + /** + * Resource mimeType as determined by the browser. + */ + public void setMimeType(String mimeType) { + this.mimeType = mimeType; + } + + /** + * Refined HTTP request headers that were actually transmitted over the network. + */ + public Map getRequestHeaders() { + return requestHeaders; + } + + /** + * Refined HTTP request headers that were actually transmitted over the network. + */ + public void setRequestHeaders(Map requestHeaders) { + this.requestHeaders = requestHeaders; + } + + /** + * HTTP request headers text. + */ + public String getRequestHeadersText() { + return requestHeadersText; + } + + /** + * HTTP request headers text. + */ + public void setRequestHeadersText(String requestHeadersText) { + this.requestHeadersText = requestHeadersText; + } + + /** + * Specifies whether physical connection was actually reused for this request. + */ + public Boolean getConnectionReused() { + return connectionReused; + } + + /** + * Specifies whether physical connection was actually reused for this request. + */ + public void setConnectionReused(Boolean connectionReused) { + this.connectionReused = connectionReused; + } + + /** + * Physical connection id that was actually used for this request. + */ + public Double getConnectionId() { + return connectionId; + } + + /** + * Physical connection id that was actually used for this request. + */ + public void setConnectionId(Double connectionId) { + this.connectionId = connectionId; + } + + /** + * Remote IP address. + */ + public String getRemoteIPAddress() { + return remoteIPAddress; + } + + /** + * Remote IP address. + */ + public void setRemoteIPAddress(String remoteIPAddress) { + this.remoteIPAddress = remoteIPAddress; + } + + /** + * Remote port. + */ + public Integer getRemotePort() { + return remotePort; + } + + /** + * Remote port. + */ + public void setRemotePort(Integer remotePort) { + this.remotePort = remotePort; + } + + /** + * Specifies that the request was served from the disk cache. + */ + public Boolean getFromDiskCache() { + return fromDiskCache; + } + + /** + * Specifies that the request was served from the disk cache. + */ + public void setFromDiskCache(Boolean fromDiskCache) { + this.fromDiskCache = fromDiskCache; + } + + /** + * Specifies that the request was served from the ServiceWorker. + */ + public Boolean getFromServiceWorker() { + return fromServiceWorker; + } + + /** + * Specifies that the request was served from the ServiceWorker. + */ + public void setFromServiceWorker(Boolean fromServiceWorker) { + this.fromServiceWorker = fromServiceWorker; + } + + /** + * Total number of bytes received for this request so far. + */ + public Double getEncodedDataLength() { + return encodedDataLength; + } + + /** + * Total number of bytes received for this request so far. + */ + public void setEncodedDataLength(Double encodedDataLength) { + this.encodedDataLength = encodedDataLength; + } + + /** + * Timing information for the given request. + */ + public ResourceTiming getTiming() { + return timing; + } + + /** + * Timing information for the given request. + */ + public void setTiming(ResourceTiming timing) { + this.timing = timing; + } + + /** + * Protocol used to fetch this request. + */ + public String getProtocol() { + return protocol; + } + + /** + * Protocol used to fetch this request. + */ + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + /** + * Security state of the request resource. + */ + public SecurityState getSecurityState() { + return securityState; + } + + /** + * Security state of the request resource. + */ + public void setSecurityState(SecurityState securityState) { + this.securityState = securityState; + } + + /** + * Security details for the request. + */ + public SecurityDetails getSecurityDetails() { + return securityDetails; + } + + /** + * Security details for the request. + */ + public void setSecurityDetails(SecurityDetails securityDetails) { + this.securityDetails = securityDetails; + } + + public static Response parseResponse(JsonInput input) { + Response response; + input.beginObject(); + + String responseUrl = null; + + Number status = null; + + String statusText = null; + + Map responseHeaders = null; + + String headersText = null; + + String mimeType = null; + + Map requestHeaders = null; + + String requestHeadersText = null; + + Boolean connectionReused = null; + + Double connectionId = null; + + String remoteIPAddress = null; + + Integer remotePort = null; + + Boolean fromDiskCache = null; + + Boolean fromServiceWorker = null; + + Double encodedDataLength = null; + + ResourceTiming timing = null; + + String protocol = null; + + SecurityState securityState = null; + + SecurityDetails securityDetails = null; + + while (input.hasNext()) { + switch (input.nextName()) { + case "url": + responseUrl = input.nextString(); + break; + case "status": + status = input.nextNumber(); + break; + case "statusText": + statusText = input.nextString(); + break; + case "headers": + responseHeaders = JsonInputConverter.extractMap(input); + break; + case "headersText": + headersText = input.nextString(); + break; + case "mimeType": + mimeType = input.nextString(); + break; + case "requestHeaders": + requestHeaders = JsonInputConverter.extractMap(input); + break; + case "requestHeadersText": + requestHeadersText = input.nextString(); + break; + case "connectionReused": + connectionReused = input.nextBoolean(); + break; + case "connectionId": + connectionId = JsonInputConverter.extractDouble(input); + break; + case "remoteIPAddress": + remoteIPAddress = input.nextString(); + break; + case "remotePort": + remotePort = JsonInputConverter.extractInt(input); + break; + case "fromDiskCache": + fromDiskCache = input.nextBoolean(); + break; + case "fromServiceWorker": + fromDiskCache = input.nextBoolean(); + break; + case "encodedDataLength": + encodedDataLength = JsonInputConverter.extractDouble(input); + break; + case "protocol": + protocol = input.nextString(); + break; + case "securityState": + securityState = SecurityState.valueOf(input.nextString()); + break; + case "securityDetails": + securityDetails = SecurityDetails.parseSecurityDetails(input); + break; + default: + input.skipValue(); + break; + } + } + + response = + new Response(responseUrl, Integer.valueOf(String.valueOf(status)), statusText, + responseHeaders, headersText, mimeType, requestHeaders, + requestHeadersText, connectionReused, + connectionId, remoteIPAddress, + remotePort, fromDiskCache, + fromServiceWorker, encodedDataLength, timing, + protocol, securityState, securityDetails); + return response; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/ResponseBody.java b/java/client/src/org/openqa/selenium/devtools/network/model/ResponseBody.java new file mode 100644 index 0000000000000..557516b36ddf5 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/ResponseBody.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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +/** + * Object for storing Network response + */ +public class ResponseBody { + + /** + * Response body + */ + private final String body; + + /** + * True, if content was sent as base64 + */ + private final Boolean base64Encoded; + + private ResponseBody(String body, Boolean base64Encoded) { + this.body = requireNonNull(body, "'body' is required for ResponseBody"); + this.base64Encoded = requireNonNull(base64Encoded, "'base64Encoded' is required for ResponseBody"); + } + + private static ResponseBody fromJson(JsonInput input) { + String body = input.nextString(); + Boolean base64Encoded = null; + + while (input.hasNext()) { + + switch (input.nextName()) { + case "base64Encoded": + base64Encoded = input.nextBoolean(); + break; + + default: + input.skipValue(); + break; + } + } + + return new ResponseBody(body, base64Encoded); + } + + public String getBody() { + return body; + } + + public Boolean getBase64Encoded() { + return base64Encoded; + } + + @Override + public String toString() { + return "ResponseBody{" + + "body='" + body + '\'' + + ", base64Encoded=" + base64Encoded + + '}'; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/ResponseReceived.java b/java/client/src/org/openqa/selenium/devtools/network/model/ResponseReceived.java new file mode 100644 index 0000000000000..f7ebbd45b4835 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/ResponseReceived.java @@ -0,0 +1,139 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +public class ResponseReceived { + + /** + * Request identifier. + */ + private final RequestId requestId; + + /** + * Loader identifier. Empty string if the request is fetched from worker. + */ + private final LoaderId loaderId; + + /** + * TimeStamp + */ + private final MonotonicTime timestamp; + + + /** + * Resource Type + */ + private final ResourceType type; + + /** + * Response data. + */ + private final Response response; + + /** + * Frame identifier. + * Optional + */ + private final String frameId; + + private ResponseReceived(RequestId requestId, + LoaderId loaderId, + MonotonicTime timestamp, + ResourceType type, + Response response, String frameId) { + this.requestId = requireNonNull(requestId, "'requestId' is required for ResponseReceived"); + this.loaderId = requireNonNull(loaderId, "'loaderId' is required for ResponseReceived"); + this.timestamp = requireNonNull(timestamp, "'timestamp' is required for ResponseReceived"); + this.type = requireNonNull(type, "'type' is required for ResponseReceived"); + this.response = requireNonNull(response, "'response' is required for ResponseReceived"); + this.frameId = frameId; + + } + + public static ResponseReceived fromJson(JsonInput input) { + RequestId requestId = new RequestId(input.nextString()); + LoaderId loaderId = null; + MonotonicTime timestamp = null; + ResourceType type = null; + Response response = null; + String frameId = null; + while (input.hasNext()) { + switch (input.nextName()) { + case "loaderId": + loaderId = new LoaderId(input.nextString()); + break; + case "timestamp": + timestamp = MonotonicTime.parse(input.nextNumber()); + break; + case "response": + response = Response.parseResponse(input); + break; + case "type": + type = ResourceType.valueOf(input.nextString()); + break; + case "frameId": + frameId = input.nextString(); + break; + default: + input.skipValue(); + break; + } + } + return new ResponseReceived(requestId, loaderId, timestamp, type, response, frameId); + } + + public RequestId getRequestId() { + return requestId; + } + + public LoaderId getLoaderId() { + return loaderId; + } + + public MonotonicTime getTimestamp() { + return timestamp; + } + + public ResourceType getType() { + return type; + } + + public Response getResponse() { + return response; + } + + public String getFrameId() { + return frameId; + } + + @Override + public String toString() { + return "ResponseReceived{" + + "requestId=" + requestId + + ", loaderId=" + loaderId + + ", timestamp=" + timestamp.getTimeStamp().toString() + + ", type=" + type + + ", response=" + response + + ", frameId='" + frameId + '\'' + + '}'; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/SearchMatch.java b/java/client/src/org/openqa/selenium/devtools/network/model/SearchMatch.java new file mode 100644 index 0000000000000..e20f2a6ee3691 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/SearchMatch.java @@ -0,0 +1,51 @@ +// 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.devtools.network.model; + +/** + * Search match for resource + */ +public class SearchMatch { + + /** + * Line number in resource content + */ + private Double lineNumber; + + /** + * Line with match content + */ + private String lineContent; + + public Double getLineNumber() { + return lineNumber; + } + + public void setLineNumber(Double lineNumber) { + this.lineNumber = lineNumber; + } + + public String getLineContent() { + return lineContent; + } + + public void setLineContent(String lineContent) { + this.lineContent = lineContent; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/SecurityDetails.java b/java/client/src/org/openqa/selenium/devtools/network/model/SecurityDetails.java new file mode 100644 index 0000000000000..4efdaca980540 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/SecurityDetails.java @@ -0,0 +1,369 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +import java.util.ArrayList; +import java.util.List; + +/** + * Security details about a request + */ +public class SecurityDetails { + + private String protocol; + + private String keyExchange; + + private String keyExchangeGroup; + + private String cipher; + + private String mac; + + private Integer certificateId; + + private String subjectName; + + private List sanList; + + private String issuer; + + private Double validFrom; + + private Double validTo; + + private List signedCertificateTimestampList; + + private CertificateTransparencyCompliance certificateTransparencyCompliance; + + private SecurityDetails(String protocol, String keyExchange, String keyExchangeGroup, + String cipher, String mac, Integer certificateId, + String subjectName, List sanList, String issuer, + Double validFrom, Double validTo, + List signedCertificateTimestampList, + CertificateTransparencyCompliance certificateTransparencyCompliance) { + this.protocol = requireNonNull(protocol, "'protocol' is required for SecurityDetails"); + this.keyExchange = requireNonNull(keyExchange, "'keyExchange' is required for SecurityDetails"); + this.keyExchangeGroup = keyExchangeGroup; + this.cipher = requireNonNull(cipher, "'cipher' is required for SecurityDetails"); + this.mac = mac; + this.certificateId = + requireNonNull(certificateId, "'certificateId' is required for SecurityDetails"); + this.subjectName = requireNonNull(subjectName, "'subjectName' is required for SecurityDetails"); + this.sanList = requireNonNull(sanList, "'sanList' is required for SecurityDetails"); + this.issuer = requireNonNull(issuer, "'issuer' is required for SecurityDetails"); + this.validFrom = requireNonNull(validFrom, "'validFrom' is required for SecurityDetails"); + this.validTo = requireNonNull(validTo, "'validTo' is required for SecurityDetails"); + this.signedCertificateTimestampList = + requireNonNull(signedCertificateTimestampList, + "'signedCertificateTimestampList' is required for SecurityDetails"); + this.certificateTransparencyCompliance = + requireNonNull(certificateTransparencyCompliance, + "'certificateTransparencyCompliance' is required for SecurityDetails"); + } + + /** + * Protocol name (e.g. "TLS 1.2" or "QUIC"). + */ + public String getProtocol() { + return protocol; + } + + /** + * Protocol name (e.g. "TLS 1.2" or "QUIC"). + */ + public void setProtocol(String protocol) { + this.protocol = protocol; + } + + /** + * Key Exchange used by the connection, or the empty string if not applicable. + */ + public String getKeyExchange() { + return keyExchange; + } + + /** + * Key Exchange used by the connection, or the empty string if not applicable. + */ + public void setKeyExchange(String keyExchange) { + this.keyExchange = keyExchange; + } + + /** + * (EC)DH group used by the connection, if applicable. + */ + public String getKeyExchangeGroup() { + return keyExchangeGroup; + } + + /** + * (EC)DH group used by the connection, if applicable. + */ + public void setKeyExchangeGroup(String keyExchangeGroup) { + this.keyExchangeGroup = keyExchangeGroup; + } + + /** + * Cipher name. + */ + public String getCipher() { + return cipher; + } + + /** + * Cipher name. + */ + public void setCipher(String cipher) { + this.cipher = cipher; + } + + /** + * TLS MAC. Note that AEAD ciphers do not have separate MACs. + */ + public String getMac() { + return mac; + } + + /** + * TLS MAC. Note that AEAD ciphers do not have separate MACs. + */ + public void setMac(String mac) { + this.mac = mac; + } + + /** + * Certificate ID value. + */ + public Integer getCertificateId() { + return certificateId; + } + + /** + * Certificate ID value. + */ + public void setCertificateId(Integer certificateId) { + this.certificateId = certificateId; + } + + /** + * Certificate subject name. + */ + public String getSubjectName() { + return subjectName; + } + + /** + * Certificate subject name. + */ + public void setSubjectName(String subjectName) { + this.subjectName = subjectName; + } + + /** + * Subject Alternative Name (SAN) DNS names and IP addresses. + */ + public List getSanList() { + return sanList; + } + + /** + * Subject Alternative Name (SAN) DNS names and IP addresses. + */ + public void setSanList(List sanList) { + this.sanList = sanList; + } + + /** + * Name of the issuing CA. + */ + public String getIssuer() { + return issuer; + } + + /** + * Name of the issuing CA. + */ + public void setIssuer(String issuer) { + this.issuer = issuer; + } + + /** + * Certificate valid from date. + */ + public Double getValidFrom() { + return validFrom; + } + + /** + * Certificate valid from date. + */ + public void setValidFrom(Double validFrom) { + this.validFrom = validFrom; + } + + /** + * Certificate valid to (expiration) date + */ + public Double getValidTo() { + return validTo; + } + + /** + * Certificate valid to (expiration) date + */ + public void setValidTo(Double validTo) { + this.validTo = validTo; + } + + /** + * List of signed certificate timestamps (SCTs). + */ + public List getSignedCertificateTimestampList() { + return signedCertificateTimestampList; + } + + /** + * List of signed certificate timestamps (SCTs). + */ + public void setSignedCertificateTimestampList( + List signedCertificateTimestampList) { + this.signedCertificateTimestampList = signedCertificateTimestampList; + } + + /** + * Whether the request complied with Certificate Transparency policy + */ + public CertificateTransparencyCompliance getCertificateTransparencyCompliance() { + return certificateTransparencyCompliance; + } + + /** + * Whether the request complied with Certificate Transparency policy + */ + public void setCertificateTransparencyCompliance( + CertificateTransparencyCompliance certificateTransparencyCompliance) { + this.certificateTransparencyCompliance = certificateTransparencyCompliance; + } + + public static SecurityDetails parseSecurityDetails(JsonInput input) { + + SecurityDetails securityDetails = null; + + String protocol = null; + + String keyExchange = null; + + String keyExchangeGroup = null; + + String cipher = null; + + String mac = null; + + Number certificateId = null; + + String subjectName = null; + + List sanList = null; + + String issuer = null; + + Number validFrom = null; + + Number validTo = null; + + List signedCertificateTimestampList = null; + + CertificateTransparencyCompliance certificateTransparencyCompliance = null; + + input.beginObject(); + + while (input.hasNext()) { + + switch (input.nextName()) { + case "protocol": + protocol = input.nextString(); + break; + case "keyExchange": + keyExchange = input.nextString(); + break; + case "keyExchangeGroup": + keyExchangeGroup = input.nextString(); + break; + case "cipher": + cipher = input.nextString(); + break; + case "mac": + mac = input.nextString(); + break; + case "certificateId": + certificateId = input.nextNumber(); + break; + case "subjectName": + subjectName = input.nextString(); + break; + case "sanList": + input.beginArray(); + sanList = new ArrayList<>(); + while (input.hasNext()) { + sanList.add(input.nextString()); + } + input.endArray(); + break; + case "issuer": + issuer = input.nextString(); + break; + case "validFrom": + validFrom = input.nextNumber(); + break; + case "validTo": + validTo = input.nextNumber(); + break; + case "signedCertificateTimestampList": + input.beginArray(); + signedCertificateTimestampList = new ArrayList<>(); + while (input.hasNext()) { + signedCertificateTimestampList + .add(SignedCertificateTimestamp.parseSignedCertificateTimestamp(input)); + } + input.endArray(); + break; + case "certificateTransparencyCompliance": + certificateTransparencyCompliance = + CertificateTransparencyCompliance.fromString(input.nextString()); + break; + default: + input.skipValue(); + break; + } + + } + + return new SecurityDetails(protocol, keyExchange, keyExchangeGroup, cipher, mac, + Integer.valueOf(String.valueOf(certificateId)), subjectName, sanList, + issuer, + validFrom != null ? Double.valueOf(String.valueOf(validFrom)) : null, + validTo != null ? Double.valueOf(String.valueOf(validTo)) : null, + signedCertificateTimestampList, certificateTransparencyCompliance); + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/SecurityState.java b/java/client/src/org/openqa/selenium/devtools/network/model/SecurityState.java new file mode 100644 index 0000000000000..a5c48fbb0001f --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/SecurityState.java @@ -0,0 +1,31 @@ +// 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.devtools.network.model; + +/** + * The security level of a page or resource + */ +public enum SecurityState { + + unknown, + neutral, + insecure, + secure, + info + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/SignedCertificateTimestamp.java b/java/client/src/org/openqa/selenium/devtools/network/model/SignedCertificateTimestamp.java new file mode 100644 index 0000000000000..4612e21ea49ff --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/SignedCertificateTimestamp.java @@ -0,0 +1,238 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +/** + * Details of a signed certificate timestamp (SCT) + */ +public class SignedCertificateTimestamp { + + private String status; + + private String origin; + + private String logDescription; + + private String logId; + + private MonotonicTime timestamp; + + private String hashAlgorithm; + + private String signatureAlgorithm; + + private String signatureData; + + private SignedCertificateTimestamp(String status, String origin, String logDescription, + String logId, MonotonicTime timestamp, String hashAlgorithm, + String signatureAlgorithm, String signatureData) { + this.status = requireNonNull(status, "'status' is required for SignedCertificateTimestamp"); + this.origin = requireNonNull(origin, "'origin' is required for SignedCertificateTimestamp"); + this.logDescription = + requireNonNull(logDescription, + "'logDescription' is required for SignedCertificateTimestamp"); + this.logId = requireNonNull(logId, "'logId' is required for SignedCertificateTimestamp"); + this.timestamp = + requireNonNull(timestamp, "'timestamp' is required for SignedCertificateTimestamp"); + this.hashAlgorithm = + requireNonNull(hashAlgorithm, "'hashAlgorithm' is required for SignedCertificateTimestamp"); + this.signatureAlgorithm = + requireNonNull(signatureAlgorithm, + "'signatureAlgorithm' is required for SignedCertificateTimestamp"); + this.signatureData = + requireNonNull(signatureData, "'signatureData' is required for SignedCertificateTimestamp"); + } + + /** + * Validation status. + */ + public String getStatus() { + return status; + } + + /** + * Validation status. + */ + public void setStatus(String status) { + this.status = status; + } + + /** + * Origin. + */ + public String getOrigin() { + return origin; + } + + /** + * Origin. + */ + public void setOrigin(String origin) { + this.origin = origin; + } + + /** + * Log name / description. + */ + public String getLogDescription() { + return logDescription; + } + + /** + * Log name / description. + */ + public void setLogDescription(String logDescription) { + this.logDescription = logDescription; + } + + /** + * Log ID. + */ + public String getLogId() { + return logId; + } + + /** + * Log ID. + */ + public void setLogId(String logId) { + this.logId = logId; + } + + /** + * Issuance date. + */ + public MonotonicTime getTimestamp() { + return timestamp; + } + + /** + * Issuance date. + */ + public void setTimestamp(MonotonicTime timestamp) { + this.timestamp = timestamp; + } + + /** + * Hash algorithm. + */ + public String getHashAlgorithm() { + return hashAlgorithm; + } + + /** + * Hash algorithm. + */ + public void setHashAlgorithm(String hashAlgorithm) { + this.hashAlgorithm = hashAlgorithm; + } + + /** + * Signature algorithm. + */ + public String getSignatureAlgorithm() { + return signatureAlgorithm; + } + + /** + * Signature algorithm. + */ + public void setSignatureAlgorithm(String signatureAlgorithm) { + this.signatureAlgorithm = signatureAlgorithm; + } + + /** + * Signature data. + */ + public String getSignatureData() { + return signatureData; + } + + /** + * Signature data. + */ + public void setSignatureData(String signatureData) { + this.signatureData = signatureData; + } + + public static SignedCertificateTimestamp parseSignedCertificateTimestamp(JsonInput input) { + + String status = null; + + String origin = null; + + String logDescription = null; + + String logId = null; + + MonotonicTime timestamp = null; + + String hashAlgorithm = null; + + String signatureAlgorithm = null; + + String signatureData = null; + + input.beginObject(); + + while (input.hasNext()) { + + switch (input.nextName()) { + case "status": + status = input.nextString(); + break; + case "origin": + origin = input.nextString(); + break; + case "logDescription": + logDescription = input.nextString(); + break; + case "logId": + logId = input.nextString(); + break; + case "timestamp": + timestamp = MonotonicTime.parse(input.nextNumber()); + break; + case "hashAlgorithm": + hashAlgorithm = input.nextString(); + break; + case "signatureAlgorithm": + signatureAlgorithm = input.nextString(); + break; + case "signatureData": + signatureData = input.nextString(); + break; + default: + input.skipValue(); + break; + } + + } + + input.endObject(); + + return new SignedCertificateTimestamp(status, origin, logDescription, logId, + timestamp, hashAlgorithm, + signatureAlgorithm, signatureData); + + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeError.java b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeError.java new file mode 100644 index 0000000000000..e83e37688744f --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeError.java @@ -0,0 +1,107 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +/** + * Information about a signed exchange response + */ +public class SignedExchangeError { + + private String message; + + private Integer signatureIndex; + + private SignedExchangeErrorField errorField; + + private SignedExchangeError(String message, Integer signatureIndex, + SignedExchangeErrorField errorField) { + this.message = requireNonNull(message, "'message' is required for SignedExchangeError"); + this.signatureIndex = signatureIndex; + this.errorField = errorField; + } + + /** + * Error message. + */ + public String getMessage() { + return message; + } + + /** + * Error message. + */ + public void setMessage(String message) { + this.message = message; + } + + /** + * The index of the signature which caused the error. + */ + public Integer getSignatureIndex() { + return signatureIndex; + } + + /** + * The index of the signature which caused the error. + */ + public void setSignatureIndex(Integer signatureIndex) { + this.signatureIndex = signatureIndex; + } + + /** + * The field which caused the error. + */ + public SignedExchangeErrorField getErrorField() { + return errorField; + } + + /** + * The field which caused the error. + */ + public void setErrorField(SignedExchangeErrorField errorField) { + this.errorField = errorField; + } + + public static SignedExchangeError parseSignedExchangeError(JsonInput input) { + + String message = null; + Number signatureIndex = null; + SignedExchangeErrorField errorField = null; + + switch (input.nextName()) { + case "message": + message = input.nextString(); + break; + case "signatureIndex": + signatureIndex = input.nextNumber(); + break; + case "errorField": + errorField = SignedExchangeErrorField.valueOf(input.nextString()); + break; + default: + input.skipValue(); + break; + } + return new SignedExchangeError(message, Integer.valueOf(String.valueOf(signatureIndex)), + errorField); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeErrorField.java b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeErrorField.java new file mode 100644 index 0000000000000..bb98c55fe96d2 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeErrorField.java @@ -0,0 +1,32 @@ +// 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.devtools.network.model; + +/** + * Field type for a signed exchange related error + */ +public enum SignedExchangeErrorField { + + signatureSig, + signatureIntegrity, + signatureCertUrl, + signatureCertSha256, + signatureValidityUrl, + signatureTimestamps + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeHeader.java b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeHeader.java new file mode 100644 index 0000000000000..fa62ee49dfccc --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeHeader.java @@ -0,0 +1,177 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInputConverter; +import org.openqa.selenium.json.JsonInput; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * Information about a signed exchange header + */ +public class SignedExchangeHeader { + + private String requestUrl; + + private String requestMethod; + + private Integer responseCode; + + private Map responseHeaders; + + private List signatures; + + private SignedExchangeHeader(String requestUrl, String requestMethod, Integer responseCode, + Map responseHeaders, + List signatures) { + this.requestUrl = + requireNonNull(requestUrl, "'requestUrl' is required for SignedExchangeHeader"); + this.requestMethod = + requireNonNull(requestMethod, "'requestMethod' is required for SignedExchangeHeader"); + this.responseCode = + requireNonNull(responseCode, "'responseCode' is required for SignedExchangeHeader"); + this.responseHeaders = + requireNonNull(responseHeaders, "'responseHeaders' is required for SignedExchangeHeader"); + this.signatures = + requireNonNull(signatures, "'signatures' is required for SignedExchangeHeader"); + } + + /** + * Signed exchange request URL. + */ + public String getRequestUrl() { + return requestUrl; + } + + /** + * Signed exchange request URL. + */ + public void setRequestUrl(String requestUrl) { + this.requestUrl = requestUrl; + } + + /** + * Signed exchange request method. + */ + public String getRequestMethod() { + return requestMethod; + } + + /** + * Signed exchange request method. + */ + public void setRequestMethod(String requestMethod) { + this.requestMethod = requestMethod; + } + + /** + * Signed exchange response code. + */ + public Integer getResponseCode() { + return responseCode; + } + + /** + * Signed exchange response code. + */ + public void setResponseCode(Integer responseCode) { + this.responseCode = responseCode; + } + + /** + * Signed exchange response headers. + */ + public Map getResponseHeaders() { + return responseHeaders; + } + + /** + * Signed exchange response headers. + */ + public void setResponseHeaders(Map responseHeaders) { + this.responseHeaders = responseHeaders; + } + + /** + * Signed exchange response signature. + */ + public List getSignatures() { + return signatures; + } + + /** + * Signed exchange response signature. + */ + public void setSignatures(List signatures) { + this.signatures = signatures; + } + + public static SignedExchangeHeader parseSignedExchangeHeader(JsonInput input) { + + String requestUrl = null; + + String requestMethod = null; + + Number responseCode = null; + + Map responseHeaders = null; + + List signatures = null; + + input.beginObject(); + + while (input.hasNext()) { + + switch (input.nextName()) { + case "requestUrl": + requestUrl = input.nextString(); + break; + case "requestMethod": + requestMethod = input.nextString(); + break; + case "responseCode": + responseCode = input.nextNumber(); + break; + case "responseHeaders": + responseHeaders = JsonInputConverter.extractMap(input); + break; + case "signatures": + input.beginArray(); + signatures = new ArrayList<>(); + while (input.hasNext()) { + signatures.add(SignedExchangeSignature.parseSignedExchangeSignature(input)); + } + input.endArray(); + break; + default: + input.skipValue(); + break; + } + + } + + return new SignedExchangeHeader(requestUrl, requestMethod, + Integer.valueOf(String.valueOf(responseCode)), responseHeaders, + signatures); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeInfo.java b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeInfo.java new file mode 100644 index 0000000000000..0237589931459 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeInfo.java @@ -0,0 +1,107 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import java.util.List; + +/** + * Information about a signed exchange response + */ +public class SignedExchangeInfo { + + private Response outerResponse; + + private SignedExchangeHeader header; + + private SecurityDetails securityDetails; + + private List errors; + + public SignedExchangeInfo() { + } + + SignedExchangeInfo(Response outerResponse, + SignedExchangeHeader header, + SecurityDetails securityDetails, + List errors) { + this.outerResponse = + requireNonNull(outerResponse, "'outerResponse' is required for SignedExchangeInfo"); + this.header = header; + this.securityDetails = securityDetails; + this.errors = errors; + } + + /** + * The outer response of signed HTTP exchange which was received from network. + */ + public Response getOuterResponse() { + return outerResponse; + } + + /** + * The outer response of signed HTTP exchange which was received from network. + */ + public void setOuterResponse(Response outerResponse) { + this.outerResponse = outerResponse; + } + + /** + * Information about the signed exchange header. + */ + public SignedExchangeHeader getHeader() { + return header; + } + + /** + * Information about the signed exchange header. + */ + public void setHeader(SignedExchangeHeader header) { + this.header = header; + } + + /** + * Security details for the signed exchange header. + */ + public SecurityDetails getSecurityDetails() { + return securityDetails; + } + + /** + * Security details for the signed exchange header. + */ + public void setSecurityDetails(SecurityDetails securityDetails) { + this.securityDetails = securityDetails; + } + + /** + * Errors occurred while handling the signed exchagne. + */ + public List getErrors() { + return errors; + } + + /** + * Errors occurred while handling the signed exchagne. + */ + public void setErrors(List errors) { + this.errors = errors; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeReceived.java b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeReceived.java new file mode 100644 index 0000000000000..3409dae082159 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeReceived.java @@ -0,0 +1,100 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +import java.util.ArrayList; +import java.util.List; + +/** + * Object for storing Network.signedExchangeReceived response + */ +public class SignedExchangeReceived { + + /** + * Request identifier + */ + private final RequestId requestId; + + /** + * Information about the signed exchange response + */ + private final SignedExchangeInfo info; + + private SignedExchangeReceived(RequestId requestId, + SignedExchangeInfo info) { + this.requestId = + requireNonNull(requestId, "'requestId' is required for SignedExchangeReceived"); + this.info = info; + } + + private static SignedExchangeReceived fromJson(JsonInput input) { + RequestId requestId = new RequestId(input.nextString()); + SignedExchangeInfo info = null; + + while (input.hasNext()) { + + switch (input.nextName()) { + case "info": + input.beginObject(); + + Response outerResponse = null; + SignedExchangeHeader header = null; + SecurityDetails securityDetails = null; + List errors = null; + + while (input.hasNext()) { + switch (input.nextName()) { + case "outerResponse": + outerResponse = Response.parseResponse(input); + break; + case "header": + header = SignedExchangeHeader.parseSignedExchangeHeader(input); + break; + case "securityDetails": + securityDetails = SecurityDetails.parseSecurityDetails(input); + break; + case "errors": + input.beginArray(); + errors = new ArrayList<>(); + while (input.hasNext()) { + errors.add(SignedExchangeError.parseSignedExchangeError(input)); + } + input.endArray(); + break; + default: + input.skipValue(); + break; + + } + } + info = new SignedExchangeInfo(outerResponse, header, securityDetails, errors); + break; + + default: + input.skipValue(); + break; + } + } + + return new SignedExchangeReceived(requestId, info); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeSignature.java b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeSignature.java new file mode 100644 index 0000000000000..7bb845420a538 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/SignedExchangeSignature.java @@ -0,0 +1,261 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +import java.util.ArrayList; +import java.util.List; + +/** + * Information about a signed exchange signature + */ +public class SignedExchangeSignature { + + private String label; + + private String signature; + + private String integrity; + + private String certUrl; + + private String certSha256; + + private String validityUrl; + + private MonotonicTime date; + + private MonotonicTime expires; + + private List certificates; + + private SignedExchangeSignature(String label, String signature, String integrity, + String certUrl, String certSha256, String validityUrl, + MonotonicTime date, MonotonicTime expires, + List certificates) { + this.label = requireNonNull(label, "'label' is required for SignedExchangeSignature"); + this.signature = + requireNonNull(signature, "'signature' is required for SignedExchangeSignature"); + this.integrity = + requireNonNull(integrity, "'integrity' is required for SignedExchangeSignature"); + this.certUrl = certUrl; + this.certSha256 = certSha256; + this.validityUrl = + requireNonNull(validityUrl, "'validityUrl' is required for SignedExchangeSignature"); + this.date = requireNonNull(date, "'date' is required for SignedExchangeSignature"); + this.expires = requireNonNull(expires, "'expires' is required for SignedExchangeSignature"); + this.certificates = certificates; + } + + /** + * Signed exchange signature label. + */ + public String getLabel() { + return label; + } + + /** + * Signed exchange signature label. + */ + public void setLabel(String label) { + this.label = label; + } + + /** + * The hex string of signed exchange signature. + */ + public String getSignature() { + return signature; + } + + /** + * The hex string of signed exchange signature. + */ + public void setSignature(String signature) { + this.signature = signature; + } + + /** + * Signed exchange signature integrity. + */ + public String getIntegrity() { + return integrity; + } + + /** + * Signed exchange signature integrity. + */ + public void setIntegrity(String integrity) { + this.integrity = integrity; + } + + /** + * Signed exchange signature cert Url. + */ + public String getCertUrl() { + return certUrl; + } + + /** + * Signed exchange signature cert Url. + */ + public void setCertUrl(String certUrl) { + this.certUrl = certUrl; + } + + /** + * The hex string of signed exchange signature cert sha256. + */ + public String getCertSha256() { + return certSha256; + } + + /** + * The hex string of signed exchange signature cert sha256. + */ + public void setCertSha256(String certSha256) { + this.certSha256 = certSha256; + } + + /** + * Signed exchange signature validity Url. + */ + public String getValidityUrl() { + return validityUrl; + } + + /** + * Signed exchange signature validity Url. + */ + public void setValidityUrl(String validityUrl) { + this.validityUrl = validityUrl; + } + + /** + * Signed exchange signature date. + */ + public MonotonicTime getDate() { + return date; + } + + /** + * Signed exchange signature date. + */ + public void setDate(MonotonicTime date) { + this.date = date; + } + + /** + * Signed exchange signature expires. + */ + public MonotonicTime getExpires() { + return expires; + } + + /** + * Signed exchange signature expires. + */ + public void setExpires(MonotonicTime expires) { + this.expires = expires; + } + + /** + * The encoded certificates. + */ + public List getCertificates() { + return certificates; + } + + /** + * The encoded certificates. + */ + public void setCertificates(List certificates) { + this.certificates = certificates; + } + + public static SignedExchangeSignature parseSignedExchangeSignature(JsonInput input) { + + String label = null; + + String signature = null; + + String integrity = null; + + String certUrl = null; + + String certSha256 = null; + + String validityUrl = null; + + MonotonicTime date = null; + + MonotonicTime expires = null; + + List certificates = null; + + input.beginObject(); + + while (input.hasNext()) { + + switch (input.nextName()) { + case "label": + label = input.nextString(); + break; + case "signature": + signature = input.nextString(); + break; + case "integrity": + integrity = input.nextString(); + break; + case "certUrl": + certUrl = input.nextString(); + break; + case "certSha256": + certSha256 = input.nextString(); + break; + case "validityUrl": + validityUrl = input.nextString(); + break; + case "date": + date = MonotonicTime.parse(input.nextNumber()); + break; + case "expires": + expires = MonotonicTime.parse(input.nextNumber()); + break; + case "certificates": + input.beginArray(); + certificates = new ArrayList<>(); + while (input.hasNext()) { + certificates.add(input.nextString()); + } + input.endArray(); + break; + default: + input.skipValue(); + break; + } + + } + + return new SignedExchangeSignature(label, signature, integrity, certUrl, certSha256, + validityUrl, date, expires, certificates); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/Source.java b/java/client/src/org/openqa/selenium/devtools/network/model/Source.java new file mode 100644 index 0000000000000..56490799010b5 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/Source.java @@ -0,0 +1,32 @@ +// 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.devtools.network.model; + +import java.util.Objects; + +public enum Source { + Server, + Proxy; + + public static Source getSource(String name){ + Objects.requireNonNull(name,"'name' field to find Source is mandatory"); + if (Server.name().equalsIgnoreCase(name)) return Server; + if (Proxy.name().equalsIgnoreCase(name)) return Proxy; + else throw new RuntimeException("Given value of "+name+" is not valid for Source"); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/StackTrace.java b/java/client/src/org/openqa/selenium/devtools/network/model/StackTrace.java new file mode 100644 index 0000000000000..e3da651e64d09 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/StackTrace.java @@ -0,0 +1,136 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +import java.util.ArrayList; +import java.util.List; + +/** + * Call frames for assertions or error messages. + */ +public class StackTrace { + + private String description; + + private List callFrames; + + private StackTrace parent; + + private StackTraceId parentId; + + private StackTrace(String description, + List callFrames, + StackTrace parent, + StackTraceId parentId) { + this.description = description; + this.callFrames = requireNonNull(callFrames, "'callFrames' is required for StackTrace"); + this.parent = parent; + this.parentId = parentId; + } + + /** + * String label of this stack trace. For async traces this may be a name of the function that + * initiated the async call. + */ + public String getDescription() { + return description; + } + + /** + * String label of this stack trace. For async traces this may be a name of the function that + * initiated the async call. + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * JavaScript function name. + */ + public List getCallFrames() { + return callFrames; + } + + /** + * JavaScript function name. + */ + public void setCallFrames(List callFrames) { + this.callFrames = callFrames; + } + + /** + * Asynchronous JavaScript stack trace that preceded this stack, if available. + */ + public StackTrace getParent() { + return parent; + } + + /** + * Asynchronous JavaScript stack trace that preceded this stack, if available. + */ + public void setParent(StackTrace parent) { + this.parent = parent; + } + + /** + * Asynchronous JavaScript stack trace that preceded this stack, if available. + */ + public StackTraceId getParentId() { + return parentId; + } + + /** + * Asynchronous JavaScript stack trace that preceded this stack, if available. + */ + public void setParentId(StackTraceId parentId) { + this.parentId = parentId; + } + + public static StackTrace parseStackTrace(JsonInput input) { + input.beginObject(); + String description = null; + List callFrames = null; + StackTrace parent = null; + StackTraceId parentId = null; + + while (input.hasNext()) { + switch (input.nextName()) { + case "description": + description = input.nextString(); + break; + case "callFrames": + input.beginArray(); + callFrames = new ArrayList<>(); + while (input.hasNext()) { + callFrames.add(CallFrame.parseCallFrame(input)); + } + input.endArray(); + break; + default: + input.skipValue(); + break; + } + } + input.endObject(); + return new StackTrace(description, callFrames, parent, parentId); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/StackTraceId.java b/java/client/src/org/openqa/selenium/devtools/network/model/StackTraceId.java new file mode 100644 index 0000000000000..3f693567549fc --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/StackTraceId.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.devtools.network.model; + +/** + * If `debuggerId` is set stack trace comes from another debugger and can be resolved there. This + * allows to track cross-debugger calls. See `Runtime.StackTrace` and `Debugger.paused` for usages. + */ +public class StackTraceId { + + private String id; + + private String debuggerId; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getDebuggerId() { + return debuggerId; + } + + public void setDebuggerId(String debuggerId) { + this.debuggerId = debuggerId; + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketClosed.java b/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketClosed.java new file mode 100644 index 0000000000000..292a50e3ac9cb --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketClosed.java @@ -0,0 +1,64 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +public class WebSocketClosed { + + /** + * Request identifier. + */ + private final RequestId requestId; + /** + * timeStamp + */ + private final MonotonicTime timestamp; + + private WebSocketClosed(RequestId requestId, + MonotonicTime timeStamp) { + this.requestId = requireNonNull(requestId, "'requestId' is required for WebSocketClosed"); + this.timestamp = requireNonNull(timeStamp, "'timestamp' is required for WebSocketClosed"); + } + + public RequestId getRequestId() { + return requestId; + } + + public MonotonicTime getTimestamp() { + return timestamp; + } + + public static WebSocketClosed fromJson(JsonInput input){ + RequestId requestId = new RequestId(input.nextString()); + MonotonicTime timestamp = null; + while (input.hasNext()){ + switch (input.nextName()){ + case "timestamp": + timestamp = MonotonicTime.parse(input.nextNumber()); + break; + default: + input.skipValue(); + break; + } + } + return new WebSocketClosed(requestId,timestamp); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketCreated.java b/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketCreated.java new file mode 100644 index 0000000000000..b4553177ea2b0 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketCreated.java @@ -0,0 +1,82 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +public class WebSocketCreated { + + /** + * request identifier + */ + private final RequestId requestId; + + /** + * WebSocket request Url + */ + private final String url; + + /** + * Request initiator. + * Optional + */ + private final Initiator initiator; + + + public RequestId getRequestId() { + return requestId; + } + + public String getUrl() { + return url; + } + + public Initiator getInitiator() { + return initiator; + } + + private WebSocketCreated(RequestId requestId, String url, + Initiator initiator) { + this.requestId = requireNonNull(requestId, "'requestId' is required for WebSocketCreated"); + this.url = requireNonNull(url, "'url' is required for WebSocketCreated"); + this.initiator = initiator; + } + + public static WebSocketCreated fromJson(JsonInput input) { + RequestId requestId = new RequestId(input.nextString()); + String url = null; + Initiator initiator = null; + + while (input.hasNext()) { + switch (input.nextName()) { + case "url": + url = input.nextString(); + break; + case "initiator": + initiator = Initiator.parseInitiator(input); + break; + default: + input.skipValue(); + break; + } + } + return new WebSocketCreated(requestId, url, initiator); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketFrame.java b/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketFrame.java new file mode 100644 index 0000000000000..d36b313f0f618 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketFrame.java @@ -0,0 +1,85 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +/** + * WebSocket message data. This represents an entire WebSocket message, not just a fragmented frame as the name suggests. + */ +public class WebSocketFrame { + + /** + * WebSocket message opcode. + */ + private Number opcode; + /** + * WebSocket message mask. + */ + private boolean mask; + + /** + * WebSocket message payload data. If the opcode is 1, this is a text message and payloadData is a UTF-8 string. If the opcode isn't 1, then payloadData is a base64 encoded string representing binary data. + */ + private String payloadData; + + private WebSocketFrame(Number opcode, boolean mask, String payloadData) { + this.opcode = requireNonNull(opcode, "'opcode' is required for WebSocketFrame"); + this.mask = mask; + this.payloadData = requireNonNull(payloadData, "'payloadData' is required for WebSocketFrame"); + } + + public static WebSocketFrame parse(JsonInput input) { + + Number opcode = null; + boolean mask = false; + String payloadData = null; + while (input.hasNext()) { + switch (input.nextName()) { + case "opcode": + opcode = input.nextNumber(); + break; + case "mask": + mask = input.nextBoolean(); + break; + case "payloadData": + payloadData = input.nextString(); + break; + default: + input.skipValue(); + break; + } + } + return new WebSocketFrame(opcode, mask, payloadData); + } + + public Number getOpcode() { + return opcode; + } + + public boolean isMask() { + return mask; + } + + public String getPayloadData() { + return payloadData; + } + +} diff --git a/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketFrameError.java b/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketFrameError.java new file mode 100644 index 0000000000000..d2f5afdc4da02 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/network/model/WebSocketFrameError.java @@ -0,0 +1,71 @@ +// 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.devtools.network.model; + +import static java.util.Objects.requireNonNull; + +import org.openqa.selenium.json.JsonInput; + +public class WebSocketFrameError { + + /** + * request identifier + */ + private final RequestId requestId; + + /** + * TimeStamp + */ + private final MonotonicTime monotonicTime; + + /** + * WebSocket Error Message + */ + private final String errorMessage; + + + private WebSocketFrameError(RequestId requestId, + MonotonicTime monotonicTime, + String errorMessage) { + this.requestId = requireNonNull(requestId, "'requestId' is required for WebSocketFrameError"); + this.monotonicTime = + requireNonNull(monotonicTime, "'monotonicTime' is required for WebSocketFrameError"); + this.errorMessage = + requireNonNull(errorMessage, "'errorMessage' is required for WebSocketFrameError"); + } + + public static WebSocketFrameError fromJson(JsonInput input) { + RequestId requestId = new RequestId(input.nextString()); + MonotonicTime monotonicTime = null; + String errorMessage = null; + while (input.hasNext()) { + switch (input.nextName()) { + case "monotonicTime": + monotonicTime = MonotonicTime.parse(input.nextNumber()); + break; + case "errorMessage": + errorMessage = input.nextString(); + break; + default: + input.skipValue(); + break; + } + } + return new WebSocketFrameError(requestId, monotonicTime, errorMessage); + } +} diff --git a/java/client/src/org/openqa/selenium/devtools/performance/Performance.java b/java/client/src/org/openqa/selenium/devtools/performance/Performance.java new file mode 100644 index 0000000000000..f3739bc7d9879 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/performance/Performance.java @@ -0,0 +1,78 @@ +// 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.devtools.performance; + +import static org.openqa.selenium.devtools.ConverterFunctions.map; + +import com.google.common.collect.ImmutableMap; +import com.google.common.reflect.TypeToken; + +import org.openqa.selenium.Beta; +import org.openqa.selenium.devtools.Command; +import org.openqa.selenium.devtools.performance.model.Metric; +import org.openqa.selenium.devtools.performance.model.TimeDomain; + +import java.util.List; +import java.util.Objects; + +/** + * All available DevTools Network methods and events Google Documentation + */ +public class Performance { + + /** + * Disable collecting and reporting metrics. + */ + public static Command disable() { + return new Command<>("Performance.disable", ImmutableMap.of()); + } + + /** + * Enable collecting and reporting metrics. + */ + public static Command enable() { + return new Command<>("Performance.enable", ImmutableMap.of()); + } + + /** + * Warning this is an Experimental Method + * Sets time domain to use for collecting and reporting duration metrics. Note that this must be called before enabling metrics collection. + * Calling this method while metrics collection is enabled returns an error.EXPERIMENTAL + * + * @param timeDomain - {@link TimeDomain} + */ + @Beta + public static Command setTimeDomain(TimeDomain timeDomain) { + Objects.requireNonNull(timeDomain, "'timeDomain' must be set"); + return new Command<>("Performance.setTimeDomain", + ImmutableMap.of("timeDomain", timeDomain.name())); + } + + /** + * Retrieve current values of run-time metrics. + * + * @return List of {@link List} + */ + public static Command> getMetrics() { + return new Command<>("Performance.getMetrics", ImmutableMap.of(), + map("metrics", new TypeToken>() { + }.getType())); + } + + +} diff --git a/java/client/src/org/openqa/selenium/devtools/performance/model/Metric.java b/java/client/src/org/openqa/selenium/devtools/performance/model/Metric.java new file mode 100644 index 0000000000000..ce6bbd5eb6471 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/performance/model/Metric.java @@ -0,0 +1,51 @@ +// 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.devtools.performance.model; + +/** + * This Class been created according to Google chrome documentation + * Run-time execution metric. + */ +public class Metric { + /** Metric name */ + private String name; + /** Metric value */ + private Integer value; + + public String getName() { + return name; + } + + public Integer getValue() { + return value; + } + + public void setName(String name) { + this.name = name; + } + + public void setValue(Integer value) { + this.value = value; + } + + @Override + public String toString(){ + return "{\"name\":"+getName()+",\"value\":"+getValue()+"}"; + } +} + diff --git a/java/client/src/org/openqa/selenium/devtools/performance/model/TimeDomain.java b/java/client/src/org/openqa/selenium/devtools/performance/model/TimeDomain.java new file mode 100644 index 0000000000000..cca9d211e9729 --- /dev/null +++ b/java/client/src/org/openqa/selenium/devtools/performance/model/TimeDomain.java @@ -0,0 +1,23 @@ +// 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.devtools.performance.model; + +public enum TimeDomain { + timeTicks, + threadTicks; +} diff --git a/java/client/src/org/openqa/selenium/json/JsonInputConverter.java b/java/client/src/org/openqa/selenium/json/JsonInputConverter.java new file mode 100644 index 0000000000000..24f9ef55715ba --- /dev/null +++ b/java/client/src/org/openqa/selenium/json/JsonInputConverter.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.json; + +import java.util.HashMap; +import java.util.Map; + +public class JsonInputConverter { + public static Double extractDouble(JsonInput input){ + Number number = input.nextNumber(); + return (null != number) ? number.doubleValue() : null; + } + + public static Integer extractInt(JsonInput input){ + Number number = input.nextNumber(); + return (null != number) ? number.intValue() : null; + } + + public static Map extractMap(JsonInput input){ + input.beginObject(); + Map map = new HashMap<>(); + while (input.hasNext()) { + map.put(input.nextName(), input.nextString()); + } + input.endObject(); + return map; + } + + +} diff --git a/java/client/test/org/openqa/selenium/I18nTest.java b/java/client/test/org/openqa/selenium/I18nTest.java index d0f68a7437934..702ea1ec4d7fa 100644 --- a/java/client/test/org/openqa/selenium/I18nTest.java +++ b/java/client/test/org/openqa/selenium/I18nTest.java @@ -153,7 +153,7 @@ public void testShouldBeAbleToActivateIMEEngine() throws InterruptedException { assertThat(ime.isActivated()).isTrue(); assertThat(ime.getActiveEngine()).isEqualTo(desiredEngine); - // Send the Romaji for "Tokyo". The space at the end instructs the IME to convert the word. + // Send the Romaji for "Tokyo". The space at the end instructs the IME to transform the word. input.sendKeys("toukyou "); input.sendKeys(Keys.ENTER); @@ -184,7 +184,7 @@ public void testShouldBeAbleToInputJapanese() { // Activate IME. By default, this keycode activates IBus input for Japanese. input.sendKeys(Keys.ZENKAKU_HANKAKU); - // Send the Romaji for "Tokyo". The space at the end instructs the IME to convert the word. + // Send the Romaji for "Tokyo". The space at the end instructs the IME to transform the word. input.sendKeys("toukyou "); String elementValue = input.getAttribute("value"); diff --git a/java/client/test/org/openqa/selenium/chrome/BUCK b/java/client/test/org/openqa/selenium/chrome/BUCK index 8aefd301c2279..ab791f41d64d5 100644 --- a/java/client/test/org/openqa/selenium/chrome/BUCK +++ b/java/client/test/org/openqa/selenium/chrome/BUCK @@ -9,6 +9,7 @@ java_test( deps = [ ":tests", "//java/client/test/org/openqa/selenium:large-tests", + "//java/client/test/org/openqa/selenium/devtools:devtools", "//third_party/java/junit:junit", ], ) @@ -19,6 +20,7 @@ java_library( deps = [ "//java/client/src/org/openqa/selenium:selenium", "//java/client/src/org/openqa/selenium/chrome:chrome", + "//java/client/src/org/openqa/selenium/devtools:devtools", "//java/client/src/org/openqa/selenium/remote:remote", "//java/client/src/org/openqa/selenium/support/ui:wait", "//java/client/test/org/openqa/selenium/build:build", diff --git a/java/client/test/org/openqa/selenium/chrome/ChromeDriverTests.java b/java/client/test/org/openqa/selenium/chrome/ChromeDriverTests.java index 016036635d618..35803b546f27d 100644 --- a/java/client/test/org/openqa/selenium/chrome/ChromeDriverTests.java +++ b/java/client/test/org/openqa/selenium/chrome/ChromeDriverTests.java @@ -20,11 +20,14 @@ import org.junit.runner.RunWith; import org.junit.runners.Suite; import org.openqa.selenium.StandardSeleniumTests; +import org.openqa.selenium.devtools.DevToolsTests; + @RunWith(Suite.class) @Suite.SuiteClasses({ StandardSeleniumTests.class, - ChromeOptionsFunctionalTest.class + ChromeOptionsFunctionalTest.class, + DevToolsTests.class }) public class ChromeDriverTests { } diff --git a/java/client/test/org/openqa/selenium/devtools/BUCK b/java/client/test/org/openqa/selenium/devtools/BUCK new file mode 100644 index 0000000000000..cc41c8e001912 --- /dev/null +++ b/java/client/test/org/openqa/selenium/devtools/BUCK @@ -0,0 +1,14 @@ +java_library( + name = "devtools", + srcs = glob(["*.java"]), + deps = [ + "//java/client/src/org/openqa/selenium:selenium", + "//java/client/src/org/openqa/selenium/chrome:chrome", + "//java/client/src/org/openqa/selenium/remote:remote", + "//java/client/src/org/openqa/selenium/devtools:devtools", + "//java/client/test/org/openqa/selenium/environment:environment", + "//java/client/test/org/openqa/selenium/testing:test-base", + "//third_party/java/junit:junit", + ], + visibility = ["//java/client/test/..."], +) diff --git a/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsNetworkTest.java b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsNetworkTest.java new file mode 100644 index 0000000000000..caafe59a4a4a9 --- /dev/null +++ b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsNetworkTest.java @@ -0,0 +1,359 @@ +// 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.devtools; + + +import static org.openqa.selenium.devtools.network.Network.clearBrowserCache; +import static org.openqa.selenium.devtools.network.Network.clearBrowserCookies; +import static org.openqa.selenium.devtools.network.Network.continueInterceptedRequest; +import static org.openqa.selenium.devtools.network.Network.dataReceived; +import static org.openqa.selenium.devtools.network.Network.deleteCookies; +import static org.openqa.selenium.devtools.network.Network.disable; +import static org.openqa.selenium.devtools.network.Network.emulateNetworkConditions; +import static org.openqa.selenium.devtools.network.Network.enable; +import static org.openqa.selenium.devtools.network.Network.eventSourceMessageReceived; +import static org.openqa.selenium.devtools.network.Network.getAllCookies; +import static org.openqa.selenium.devtools.network.Network.getCertificate; +import static org.openqa.selenium.devtools.network.Network.getCookies; +import static org.openqa.selenium.devtools.network.Network.getRequestPostData; +import static org.openqa.selenium.devtools.network.Network.getResponseBody; +import static org.openqa.selenium.devtools.network.Network.loadingFailed; +import static org.openqa.selenium.devtools.network.Network.loadingFinished; +import static org.openqa.selenium.devtools.network.Network.requestIntercepted; +import static org.openqa.selenium.devtools.network.Network.requestServedFromCache; +import static org.openqa.selenium.devtools.network.Network.requestWillBeSent; +import static org.openqa.selenium.devtools.network.Network.resourceChangedPriority; +import static org.openqa.selenium.devtools.network.Network.responseReceived; +import static org.openqa.selenium.devtools.network.Network.searchInResponseBody; +import static org.openqa.selenium.devtools.network.Network.setBlockedURLs; +import static org.openqa.selenium.devtools.network.Network.setBypassServiceWorker; +import static org.openqa.selenium.devtools.network.Network.setCacheDisabled; +import static org.openqa.selenium.devtools.network.Network.setCookie; +import static org.openqa.selenium.devtools.network.Network.setDataSizeLimitsForTest; +import static org.openqa.selenium.devtools.network.Network.setExtraHTTPHeaders; +import static org.openqa.selenium.devtools.network.Network.setRequestInterception; +import static org.openqa.selenium.devtools.network.Network.setUserAgentOverride; +import static org.openqa.selenium.devtools.network.Network.signedExchangeReceived; +import static org.openqa.selenium.devtools.network.Network.webSocketClosed; +import static org.openqa.selenium.devtools.network.Network.webSocketCreated; +import static org.openqa.selenium.devtools.network.Network.webSocketFrameError; +import static org.openqa.selenium.devtools.network.Network.webSocketFrameReceived; +import static org.openqa.selenium.devtools.network.Network.webSocketFrameSent; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.Cookie; +import org.openqa.selenium.devtools.network.model.BlockedReason; +import org.openqa.selenium.devtools.network.model.ConnectionType; +import org.openqa.selenium.devtools.network.model.InterceptionStage; +import org.openqa.selenium.devtools.network.model.RequestId; +import org.openqa.selenium.devtools.network.model.RequestPattern; +import org.openqa.selenium.devtools.network.model.ResourceType; +import org.openqa.selenium.devtools.network.model.ResponseBody; +import org.openqa.selenium.remote.http.HttpMethod; + +import java.util.List; +import java.util.Optional; + +public class ChromeDevToolsNetworkTest extends ChromeDevToolsTestBase { + + @Test + public void getSetDeleteAndClearAllCookies() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + + List allCookies = devTools.send(getAllCookies()).asSeleniumCookies(); + + Assert.assertEquals(0, allCookies.size()); + + Cookie cookieToSet = + new Cookie.Builder("name", "value") + .path("/devtools/test") + .domain("localhost") + .isHttpOnly(true) + .build(); + boolean setCookie; + setCookie = devTools.send(setCookie(cookieToSet, Optional.empty())); + Assert.assertEquals(true, setCookie); + + Assert.assertEquals(1, devTools.send(getAllCookies()).asSeleniumCookies().size()); + Assert.assertEquals(0, devTools.send(getCookies(Optional.empty())).asSeleniumCookies().size()); + + devTools.send(deleteCookies("name", Optional.empty(), Optional.of("localhost"), + Optional.of("/devtools/test"))); + + devTools.send(clearBrowserCookies()); + + Assert.assertEquals(0, devTools.send(getAllCookies()).asSeleniumCookies().size()); + + setCookie = devTools.send(setCookie(cookieToSet, Optional.empty())); + Assert.assertEquals(true, setCookie); + + Assert.assertEquals(1, devTools.send(getAllCookies()).asSeleniumCookies().size()); + + } + + @Test + public void sendRequestWithUrlFiltersAndExtraHeadersAndVerifyRequests() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + + devTools.send(setBlockedURLs(ImmutableList.of("*://*/*.css"))); + + devTools.send(setExtraHTTPHeaders(ImmutableMap.of("headerName", "headerValue"))); + + devTools.addListener(loadingFailed(), loadingFailed -> { + if (loadingFailed.getResourceType().equals(ResourceType.Stylesheet)) { + Assert.assertEquals(loadingFailed.getBlockedReason(), BlockedReason.inspector); + } + }); + + devTools.addListener(requestWillBeSent(), requestWillBeSent -> Assert + .assertEquals(requestWillBeSent.getRequest().getHeaders().get("headerName"), + "headerValue")); + + devTools.addListener(dataReceived(), + dataReceived -> Assert.assertNotNull(dataReceived.getRequestId())); + + chromeDriver.get(appServer.whereIs("js/skins/lightgray/content.min.css")); + + } + + @Test + public void emulateNetworkConditionOffline() { + + devTools.send(enable(Optional.of(100000000), Optional.empty(), Optional.empty())); + + devTools.send( + emulateNetworkConditions(true, 100, 1000, 2000, Optional.of(ConnectionType.cellular3g))); + + devTools.addListener(loadingFailed(), loadingFailed -> Assert + .assertEquals(loadingFailed.getErrorText(), "net::ERR_INTERNET_DISCONNECTED")); + + chromeDriver.get(appServer.whereIs("simpleTest.html")); + + } + + @Test + public void verifyRequestReceivedFromCacheAndResponseBody() { + + final RequestId[] requestIdFromCache = new RequestId[1]; + devTools.send(enable(Optional.empty(), Optional.of(100000000), Optional.empty())); + + devTools.addListener(requestServedFromCache(), requestId -> { + Assert.assertNotNull(requestId); + requestIdFromCache[0] = requestId; + }); + + devTools.addListener(loadingFinished(), + dataReceived -> Assert.assertNotNull(dataReceived.getRequestId())); + + chromeDriver.get(appServer.whereIsSecure("simpleTest.html")); + chromeDriver.get(appServer.whereIsSecure("simpleTest.html")); + + ResponseBody responseBody = devTools.send(getResponseBody(requestIdFromCache[0])); + Assert.assertNotNull(responseBody); + + } + + @Test + public void verifySearchInResponseBody() { + + final RequestId[] requestIds = new RequestId[1]; + devTools.send(enable(Optional.empty(), Optional.of(100000000), Optional.empty())); + + devTools.addListener(responseReceived(), responseReceived -> { + Assert.assertNotNull(responseReceived); + requestIds[0] = responseReceived.getRequestId(); + }); + + chromeDriver.get(appServer.whereIs("simpleTest.html")); + + Assert.assertEquals(true, devTools.send( + searchInResponseBody(requestIds[0], "/", Optional.empty(), Optional.empty())).size() + > 0); + + } + + @Test + public void verifyCacheDisabledAndClearCache() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.of(100000000))); + + devTools.addListener(responseReceived(), responseReceived -> Assert + .assertEquals(false, responseReceived.getResponse().getFromDiskCache())); + + chromeDriver.get(appServer.whereIs("simpleTest.html")); + + devTools.send(setCacheDisabled(true)); + + chromeDriver.get(appServer.whereIs("simpleTest.html")); + + devTools.send(clearBrowserCache()); + + } + + @Test + public void verifyCertificatesAndOverrideUserAgent() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + + devTools.send(setUserAgentOverride("userAgent", Optional.empty(), Optional.empty())); + + devTools.addListener(requestWillBeSent(), + requestWillBeSent -> Assert.assertEquals("userAgent", + requestWillBeSent + .getRequest() + .getHeaders() + .get("User-Agent"))); + chromeDriver.get(appServer.whereIsSecure("simpleTest.html")); + + Assert.assertEquals(true, devTools + .send(getCertificate(appServer.whereIsSecure("simpleTest.html"))) + .size() > 0); + } + + @Test + public void verifyResponseReceivedEventAndNetworkDisable() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + devTools.addListener(responseReceived(), Assert::assertNotNull); + chromeDriver.get(appServer.whereIs("simpleTest.html")); + devTools.send(disable()); + } + + @Test + public void verifyWebSocketOperations() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + + devTools.addListener(webSocketCreated(), Assert::assertNotNull); + devTools.addListener(webSocketFrameReceived(), Assert::assertNotNull); + devTools.addListener(webSocketClosed(), Assert::assertNotNull); + devTools.addListener(webSocketFrameError(), Assert::assertNotNull); + devTools.addListener(webSocketFrameSent(), Assert::assertNotNull); + + chromeDriver.get(appServer.whereIs("simpleTest.html")); + + } + + @Test + public void verifyRequestPostData() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + + final RequestId[] requestIds = new RequestId[1]; + + devTools.addListener(requestWillBeSent(), requestWillBeSent -> { + Assert.assertNotNull(requestWillBeSent); + if (requestWillBeSent.getRequest().getMethod().equalsIgnoreCase(HttpMethod.POST.name())) { + requestIds[0] = requestWillBeSent.getRequestId(); + } + }); + + chromeDriver.get(appServer.whereIs("postForm.html")); + + chromeDriver.findElement(By.xpath("/html/body/form/input")).click(); + + Assert.assertNotNull(devTools.send(getRequestPostData(requestIds[0]))); + + } + + @Test + public void byPassServiceWorker() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + + devTools.send(setBypassServiceWorker(true)); + + System.out.println(""); + + } + + @Test + public void dataSizeLimitsForTest() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + + devTools.send(setDataSizeLimitsForTest(10000, 100000)); + + System.out.println(""); + + } + + @Test + public void verifyEventSourceMessage() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + + devTools.addListener(eventSourceMessageReceived(), Assert::assertNotNull); + + chromeDriver.get(appServer.whereIs("simpleTest.html")); + + } + + @Test + public void verifySignedExchangeReceived() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + + devTools.addListener(signedExchangeReceived(), Assert::assertNotNull); + + chromeDriver.get(appServer.whereIsSecure("simpleTest.html")); + + } + + @Test + public void verifyResourceChangedPriority() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + + devTools.addListener(resourceChangedPriority(), Assert::assertNotNull); + + chromeDriver.get(appServer.whereIsSecure("simpleTest.html")); + + } + + @Test + public void interceptRequestAndContinue() { + + devTools.send(enable(Optional.empty(), Optional.empty(), Optional.empty())); + + devTools.addListener(requestIntercepted(), + requestIntercepted -> devTools.send( + continueInterceptedRequest(requestIntercepted.getInterceptionId(), + Optional.empty(), + Optional.empty(), + Optional.empty(), Optional.empty(), + Optional.empty(), + Optional.empty(), Optional.empty()))); + + RequestPattern + requestPattern = + new RequestPattern("*.css", ResourceType.Stylesheet, InterceptionStage.HeadersReceived); + devTools.send(setRequestInterception(ImmutableList.of(requestPattern))); + + chromeDriver.get(appServer.whereIs("js/skins/lightgray/content.min.css")); + + } + +} diff --git a/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsPerformanceTest.java b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsPerformanceTest.java new file mode 100644 index 0000000000000..04a3d1eb69f9d --- /dev/null +++ b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsPerformanceTest.java @@ -0,0 +1,95 @@ +// 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.devtools; + +import static org.openqa.selenium.devtools.performance.Performance.disable; +import static org.openqa.selenium.devtools.performance.Performance.enable; +import static org.openqa.selenium.devtools.performance.Performance.getMetrics; + +import org.junit.Assert; +import org.junit.Test; +import org.openqa.selenium.devtools.performance.Performance; +import org.openqa.selenium.devtools.performance.model.Metric; +import org.openqa.selenium.devtools.performance.model.TimeDomain; + +import java.util.List; +import java.util.Objects; + + +public class ChromeDevToolsPerformanceTest extends ChromeDevToolsTestBase { + + + @Test + public void enableAndDisablePerformance() { + + devTools.send(enable()); + chromeDriver.get(appServer.whereIs("simpleTest.html")); + devTools.send(disable()); + } + + @Test + public void disablePerformance() { + + devTools.send(disable()); + chromeDriver.get(appServer.whereIs("simpleTest.html")); + devTools.send(disable()); + } + + @Test + public void setTimeDomainTimeTickPerformance() { + devTools.send(disable()); + + devTools.send(Performance.setTimeDomain(TimeDomain.timeTicks)); + devTools.send(enable()); + chromeDriver.get(appServer.whereIs("simpleTest.html")); + devTools.send(disable()); + } + + @Test + public void setTimeDomainsThreadTicksPerformance() { + devTools.send(disable()); + devTools.send(Performance.setTimeDomain(TimeDomain.threadTicks)); + devTools.send(enable()); + chromeDriver.get(appServer.whereIs("simpleTest.html")); + devTools.send(disable()); + } + + @Test + public void getMetricsByTimeTicks() { + devTools.send(Performance.setTimeDomain(TimeDomain.timeTicks)); + devTools.send(enable()); + chromeDriver.get(appServer.whereIs("simpleTest.html")); + List metrics = devTools.send(getMetrics()); + Objects.requireNonNull(metrics); + Assert.assertFalse(metrics.isEmpty()); + devTools.send(disable()); + } + + @Test + public void getMetricsByThreadTicks() { + devTools.send(Performance.setTimeDomain(TimeDomain.threadTicks)); + devTools.send(enable()); + chromeDriver.get(appServer.whereIs("simpleTest.html")); + List metrics = devTools.send(getMetrics()); + Objects.requireNonNull(metrics); + Assert.assertFalse(metrics.isEmpty()); + devTools.send(disable()); + } + + +} diff --git a/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsTestBase.java b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsTestBase.java new file mode 100644 index 0000000000000..e3f55ce130c13 --- /dev/null +++ b/java/client/test/org/openqa/selenium/devtools/ChromeDevToolsTestBase.java @@ -0,0 +1,49 @@ +// 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.devtools; + +import org.junit.After; +import org.junit.Before; +import org.openqa.selenium.chrome.ChromeDriver; + +/** + * Created by aohana + */ +public abstract class ChromeDevToolsTestBase extends DevToolsTestBase { + + ChromeDriver chromeDriver; + + @Before + public void setUp() { + + super.setUp(); + + chromeDriver = new ChromeDriver(); + devTools = chromeDriver.getDevTools(); + + devTools.createSession(); + } + + + @After + public void terminateSession() { + devTools.close(); + chromeDriver.quit(); + } + +} diff --git a/java/client/test/org/openqa/selenium/devtools/DevToolsTest.java b/java/client/test/org/openqa/selenium/devtools/DevToolsTest.java deleted file mode 100644 index e26348ac3cf33..0000000000000 --- a/java/client/test/org/openqa/selenium/devtools/DevToolsTest.java +++ /dev/null @@ -1,52 +0,0 @@ -// 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.devtools; - -import org.junit.Test; - -public class DevToolsTest { - - @Test - public void bootstrap() { -// String rawResult = "{\"id\":1,\"result\":{}}"; -// -// Connection connection = new Connection() { -// -// }; -// -// DevTools devTools = new DevTools(connection); -// Future future = devTools.listenFor(reply -> reply.getId() == 1); - } - - @Test - public void boostrap2() { -// String rawEvent = -// "{\"method\":\"Target.targetCreated\"," + -// "\"params\":{\"targetInfo\":{\"targetId\":\"92C067F8FA440F318ED1B8945C029B47\"," + -// "\"type\":\"page\",\"title\":\"\",\"url\":\"about:blank\"," + -// "\"attached\":false,\"browserContextId\":\"CBDE829A8078645243E8567CB24C3B9C\"}}}"; -// -// Connection connection = new Connection() { -// -// }; -// -// DevTools devTools = new DevTools(connection); -// devTools.on(reply -> reply.getId() == 1) - } - -} diff --git a/java/client/test/org/openqa/selenium/devtools/DevToolsTestBase.java b/java/client/test/org/openqa/selenium/devtools/DevToolsTestBase.java new file mode 100644 index 0000000000000..a1f7ce8d6fcc9 --- /dev/null +++ b/java/client/test/org/openqa/selenium/devtools/DevToolsTestBase.java @@ -0,0 +1,44 @@ +// 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.devtools; + +import org.junit.Before; +import org.openqa.selenium.environment.GlobalTestEnvironment; +import org.openqa.selenium.environment.InProcessTestEnvironment; +import org.openqa.selenium.environment.TestEnvironment; +import org.openqa.selenium.environment.webserver.AppServer; +import org.openqa.selenium.testing.Pages; + + +public abstract class DevToolsTestBase { + + DevTools devTools; + protected TestEnvironment environment; + protected AppServer appServer; + protected Pages pages; + + @Before + public void setUp() { + + environment = GlobalTestEnvironment.get(InProcessTestEnvironment.class); + appServer = environment.getAppServer(); + pages = new Pages(appServer); + + } + +} diff --git a/java/client/test/org/openqa/selenium/devtools/DevToolsTests.java b/java/client/test/org/openqa/selenium/devtools/DevToolsTests.java new file mode 100644 index 0000000000000..e7e0460f4676e --- /dev/null +++ b/java/client/test/org/openqa/selenium/devtools/DevToolsTests.java @@ -0,0 +1,30 @@ +// 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.devtools; + +import org.junit.runner.RunWith; +import org.junit.runners.Suite; + +@RunWith(Suite.class) +@Suite.SuiteClasses({ + ChromeDevToolsNetworkTest.class, + ChromeDevToolsPerformanceTest.class +}) +public class DevToolsTests { + +} diff --git a/java/server/src/org/openqa/selenium/grid/config/AnnotatedConfig.java b/java/server/src/org/openqa/selenium/grid/config/AnnotatedConfig.java index 61a576779b0b2..0f95853c6d7ee 100644 --- a/java/server/src/org/openqa/selenium/grid/config/AnnotatedConfig.java +++ b/java/server/src/org/openqa/selenium/grid/config/AnnotatedConfig.java @@ -40,7 +40,7 @@ * not stable (meaning duplicate config values may give different values each time). *

* The main use of this class is to allow an object configured using (for example) jcommander to be - * used directly within the app, without requiring intermediate support classes to convert flags to + * used directly within the app, without requiring intermediate support classes to transform flags to * config values. */ public class AnnotatedConfig implements Config {