diff --git a/java/client/src/org/openqa/selenium/devtools/idealized/Network.java b/java/client/src/org/openqa/selenium/devtools/idealized/Network.java index 8418094dd103a..234a0792d282a 100644 --- a/java/client/src/org/openqa/selenium/devtools/idealized/Network.java +++ b/java/client/src/org/openqa/selenium/devtools/idealized/Network.java @@ -59,6 +59,51 @@ public void disable() { interceptingTraffic = false; } + public static class UserAgent { + + private final String userAgent; + private final Optional acceptLanguage; + private final Optional platform; + + public UserAgent(String userAgent) { + this(userAgent, Optional.empty(), Optional.empty()); + } + + private UserAgent(String userAgent, Optional acceptLanguage, Optional platform) { + this.userAgent = userAgent; + this.acceptLanguage = acceptLanguage; + this.platform = platform; + } + + public String userAgent() { + return userAgent; + } + + public UserAgent acceptLanguage(String acceptLanguage) { + return new UserAgent(this.userAgent, Optional.of(acceptLanguage), this.platform); + } + + public Optional acceptLanguage() { + return acceptLanguage; + } + + public UserAgent platform(String platform) { + return new UserAgent(this.userAgent, this.acceptLanguage, Optional.of(platform)); + } + + public Optional platform() { + return platform; + } + } + + public void setUserAgent(String userAgent) { + devTools.send(setUserAgentOverride(new UserAgent(userAgent))); + } + + public void setUserAgent(UserAgent userAgent) { + devTools.send(setUserAgentOverride(userAgent)); + } + public void addAuthHandler(Predicate whenThisMatches, Supplier useTheseCredentials) { Require.nonNull("URI predicate", whenThisMatches); Require.nonNull("Credentials", useTheseCredentials); @@ -197,6 +242,8 @@ protected HttpRequest createHttpRequest( return req; } + protected abstract Command setUserAgentOverride(UserAgent userAgent); + protected abstract Command enableNetworkCaching(); protected abstract Command disableNetworkCaching(); diff --git a/java/client/src/org/openqa/selenium/devtools/v86/V86Network.java b/java/client/src/org/openqa/selenium/devtools/v86/V86Network.java index d4452e9965d84..867bed08b0252 100644 --- a/java/client/src/org/openqa/selenium/devtools/v86/V86Network.java +++ b/java/client/src/org/openqa/selenium/devtools/v86/V86Network.java @@ -45,6 +45,12 @@ public V86Network(DevTools devTools) { super(devTools); } + @Override + protected Command setUserAgentOverride(UserAgent userAgent) { + return org.openqa.selenium.devtools.v89.network.Network.setUserAgentOverride( + userAgent.userAgent(), userAgent.acceptLanguage(), userAgent.platform(), Optional.empty()); + } + @Override protected Command enableNetworkCaching() { return org.openqa.selenium.devtools.v86.network.Network.setCacheDisabled(false); diff --git a/java/client/src/org/openqa/selenium/devtools/v87/V87Network.java b/java/client/src/org/openqa/selenium/devtools/v87/V87Network.java index 1f24bd545b9fd..edec8a3ea4041 100644 --- a/java/client/src/org/openqa/selenium/devtools/v87/V87Network.java +++ b/java/client/src/org/openqa/selenium/devtools/v87/V87Network.java @@ -45,6 +45,12 @@ public V87Network(DevTools devTools) { super(devTools); } + @Override + protected Command setUserAgentOverride(UserAgent userAgent) { + return org.openqa.selenium.devtools.v89.network.Network.setUserAgentOverride( + userAgent.userAgent(), userAgent.acceptLanguage(), userAgent.platform(), Optional.empty()); + } + @Override protected Command enableNetworkCaching() { return org.openqa.selenium.devtools.v87.network.Network.setCacheDisabled(false); diff --git a/java/client/src/org/openqa/selenium/devtools/v88/V88Network.java b/java/client/src/org/openqa/selenium/devtools/v88/V88Network.java index ae66589b27fa5..f5c919a831a66 100644 --- a/java/client/src/org/openqa/selenium/devtools/v88/V88Network.java +++ b/java/client/src/org/openqa/selenium/devtools/v88/V88Network.java @@ -45,6 +45,12 @@ public V88Network(DevTools devTools) { super(devTools); } + @Override + protected Command setUserAgentOverride(UserAgent userAgent) { + return org.openqa.selenium.devtools.v89.network.Network.setUserAgentOverride( + userAgent.userAgent(), userAgent.acceptLanguage(), userAgent.platform(), Optional.empty()); + } + @Override protected Command enableNetworkCaching() { return org.openqa.selenium.devtools.v88.network.Network.setCacheDisabled(false); diff --git a/java/client/src/org/openqa/selenium/devtools/v89/V89Network.java b/java/client/src/org/openqa/selenium/devtools/v89/V89Network.java index 8a8a138c07332..af7533522d391 100644 --- a/java/client/src/org/openqa/selenium/devtools/v89/V89Network.java +++ b/java/client/src/org/openqa/selenium/devtools/v89/V89Network.java @@ -45,6 +45,12 @@ public V89Network(DevTools devTools) { super(devTools); } + @Override + protected Command setUserAgentOverride(UserAgent userAgent) { + return org.openqa.selenium.devtools.v89.network.Network.setUserAgentOverride( + userAgent.userAgent(), userAgent.acceptLanguage(), userAgent.platform(), Optional.empty()); + } + @Override protected Command enableNetworkCaching() { return org.openqa.selenium.devtools.v89.network.Network.setCacheDisabled(false); diff --git a/java/client/test/org/openqa/selenium/devtools/ChangeUserAgentTest.java b/java/client/test/org/openqa/selenium/devtools/ChangeUserAgentTest.java new file mode 100644 index 0000000000000..75dcde6b1476b --- /dev/null +++ b/java/client/test/org/openqa/selenium/devtools/ChangeUserAgentTest.java @@ -0,0 +1,55 @@ +// 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.assertj.core.api.Assertions.assertThat; + +import org.junit.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.devtools.idealized.Network; + +import java.util.Map; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; +import java.util.stream.Collectors; + +public class ChangeUserAgentTest extends DevToolsTestBase { + + @Test + public void canChangeUserAgent() throws InterruptedException, ExecutionException, TimeoutException { + devTools.getDomains().network().setUserAgent( + new Network.UserAgent("Camembert 1.0") + .platform("FreeBSD").acceptLanguage("da, en-gb, *")); + driver.get(appServer.whereIs("/echo")); + + Map headers = driver.findElements(By.cssSelector("#headers tr")).stream() + .map(row -> row.findElements(By.tagName("td"))) + .collect(Collectors.toMap(cells -> cells.get(0).getText(), cells -> cells.get(1).getText())); + String userAgent = driver.findElements(By.cssSelector("#headers tr")).stream() + .map(row -> row.findElements(By.tagName("td"))) + .filter(cells -> cells.get(0).getText().equals("User-Agent")) + .map(cells -> cells.get(1).getText()) + .findFirst().orElseThrow(); + assertThat(headers).containsEntry("User-Agent", "Camembert 1.0"); + assertThat(headers).containsEntry("Accept-Language", "da, en-gb;q=0.9, *;q=0.8"); + Object platform = ((JavascriptExecutor) driver).executeScript("return window.navigator.platform"); + assertThat(platform).isEqualTo("FreeBSD"); + } + +} diff --git a/java/client/test/org/openqa/selenium/environment/webserver/EchoHandler.java b/java/client/test/org/openqa/selenium/environment/webserver/EchoHandler.java new file mode 100644 index 0000000000000..c35382263edce --- /dev/null +++ b/java/client/test/org/openqa/selenium/environment/webserver/EchoHandler.java @@ -0,0 +1,74 @@ +// 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.environment.webserver; + +import static java.nio.charset.StandardCharsets.UTF_8; +import static java.time.format.DateTimeFormatter.RFC_1123_DATE_TIME; + +import com.google.common.base.Splitter; + +import org.openqa.selenium.Cookie; +import org.openqa.selenium.remote.http.Contents; +import org.openqa.selenium.remote.http.HttpHandler; +import org.openqa.selenium.remote.http.HttpRequest; +import org.openqa.selenium.remote.http.HttpResponse; + +import java.io.UncheckedIOException; +import java.time.Instant; +import java.time.ZoneId; +import java.time.ZonedDateTime; +import java.time.temporal.TemporalAccessor; +import java.util.Collection; +import java.util.Date; +import java.util.List; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.StreamSupport; + +public class EchoHandler implements HttpHandler { + + private static final String EPOCH_START = RFC_1123_DATE_TIME.format( + ZonedDateTime.ofInstant(Instant.ofEpochMilli(0), ZoneId.of("UTC"))); + private static final String RESPONSE_STRING = + "Done" + + "

Method: %s

" + + "

Headers

%s
" + + "

Body:

%s
" + + ""; + + @Override + public HttpResponse execute(HttpRequest request) throws UncheckedIOException { + HttpResponse response = new HttpResponse(); + response.setHeader("Content-Type", "text/html"); + //Dont Cache Anything at the browser + response.setHeader("Cache-Control","no-cache"); + response.setHeader("Pragma","no-cache"); + response.setHeader ("Expires", EPOCH_START); + + String method = request.getMethod().toString(); + String headers = StreamSupport.stream(request.getHeaderNames().spliterator(), false) + .flatMap(name -> StreamSupport.stream(request.getHeaders(name).spliterator(), false) + .map(value -> String.format("%s%s", name, value))) + .collect(Collectors.joining("")); + String body = Contents.utf8String(request.getContent()); + + response.setContent(Contents.string(String.format(RESPONSE_STRING, method, headers, body), UTF_8)); + + return response; + } +} diff --git a/java/client/test/org/openqa/selenium/environment/webserver/HandlersForTests.java b/java/client/test/org/openqa/selenium/environment/webserver/HandlersForTests.java index af6729a82f9d0..683d083b6f020 100644 --- a/java/client/test/org/openqa/selenium/environment/webserver/HandlersForTests.java +++ b/java/client/test/org/openqa/selenium/environment/webserver/HandlersForTests.java @@ -47,6 +47,7 @@ public HandlersForTests(String hostname, int port, Path tempPageDir) { Route route = Route.combine( Route.get("/basicAuth").to(BasicAuthHandler::new), + Route.get("/echo").to(EchoHandler::new), Route.get("/cookie").to(CookieHandler::new), Route.get("/encoding").to(EncodingHandler::new), Route.matching(req -> req.getUri().startsWith("/generated/")).to(() -> new GeneratedJsTestHandler("/generated")),