From 9e061bbd7c4a9a8f324a485bd514b9ab3290308d Mon Sep 17 00:00:00 2001 From: Delta456 Date: Fri, 28 Nov 2025 00:04:05 +0530 Subject: [PATCH 1/3] [java][BiDi] implement `browser.setDownloadBehavior` --- .../SetDownloadBehaviorParameters.java | 46 +++++ .../openqa/selenium/bidi/module/Browser.java | 5 + .../bidi/browser/BrowserCommandsTest.java | 170 +++++++++++++++++- 3 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 java/src/org/openqa/selenium/bidi/browser/SetDownloadBehaviorParameters.java diff --git a/java/src/org/openqa/selenium/bidi/browser/SetDownloadBehaviorParameters.java b/java/src/org/openqa/selenium/bidi/browser/SetDownloadBehaviorParameters.java new file mode 100644 index 0000000000000..c56133e3b7bd0 --- /dev/null +++ b/java/src/org/openqa/selenium/bidi/browser/SetDownloadBehaviorParameters.java @@ -0,0 +1,46 @@ +package org.openqa.selenium.bidi.browser; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class SetDownloadBehaviorParameters { + private final Map map = new HashMap<>(); + + public SetDownloadBehaviorParameters(Boolean allowed, String destinationFolder) { + this(allowed, destinationFolder != null ? Paths.get(destinationFolder) : null); + } + + public SetDownloadBehaviorParameters(Boolean allowed, Path destinationFolder) { + if (allowed == null) { + map.put("downloadBehavior", null); + } else if (allowed) { + if (destinationFolder == null) { + throw new IllegalArgumentException("destinationFolder is required when allowed is true"); + } + Map behavior = new HashMap<>(); + behavior.put("type", "allowed"); + behavior.put("destinationFolder", destinationFolder.toAbsolutePath().toString()); + map.put("downloadBehavior", behavior); + } else { + if (destinationFolder != null) { + throw new IllegalArgumentException( + "destinationFolder should not be provided when allowed is false"); + } + Map behavior = new HashMap<>(); + behavior.put("type", "denied"); + map.put("downloadBehavior", behavior); + } + } + + public SetDownloadBehaviorParameters userContexts(List userContexts) { + map.put("userContexts", userContexts); + return this; + } + + public Map toMap() { + return map; + } +} diff --git a/java/src/org/openqa/selenium/bidi/module/Browser.java b/java/src/org/openqa/selenium/bidi/module/Browser.java index be14ef2af666e..928c3b794a5aa 100644 --- a/java/src/org/openqa/selenium/bidi/module/Browser.java +++ b/java/src/org/openqa/selenium/bidi/module/Browser.java @@ -26,6 +26,7 @@ import org.openqa.selenium.bidi.Command; import org.openqa.selenium.bidi.HasBiDi; import org.openqa.selenium.bidi.browser.ClientWindowInfo; +import org.openqa.selenium.bidi.browser.SetDownloadBehaviorParameters; import org.openqa.selenium.json.JsonInput; public class Browser { @@ -90,4 +91,8 @@ public void removeUserContext(String userContext) { public List getClientWindows() { return bidi.send(new Command<>("browser.getClientWindows", Map.of(), clientWindowsInfoMapper)); } + + public void setDownloadBehavior(SetDownloadBehaviorParameters parameters) { + bidi.send(new Command<>("browser.setDownloadBehavior", parameters.toMap())); + } } diff --git a/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java b/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java index 98239a6732e00..7fda531ebc63e 100644 --- a/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java +++ b/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java @@ -17,14 +17,28 @@ package org.openqa.selenium.bidi.browser; -import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.Assertions.assertThat; +import static org.openqa.selenium.testing.drivers.Browser.FIREFOX; +import java.nio.file.Files; +import java.nio.file.Path; +import java.time.Duration; import java.util.List; +import java.util.stream.Collectors; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.openqa.selenium.By; +import org.openqa.selenium.TimeoutException; +import org.openqa.selenium.WindowType; +import org.openqa.selenium.bidi.browsingcontext.BrowsingContext; +import org.openqa.selenium.bidi.browsingcontext.CreateContextParameters; +import org.openqa.selenium.bidi.browsingcontext.ReadinessState; import org.openqa.selenium.bidi.module.Browser; +import org.openqa.selenium.io.TemporaryFilesystem; +import org.openqa.selenium.support.ui.WebDriverWait; import org.openqa.selenium.testing.JupiterTestBase; import org.openqa.selenium.testing.NeedsFreshDriver; +import org.openqa.selenium.testing.NotYetImplemented; class BrowserCommandsTest extends JupiterTestBase { @@ -91,4 +105,158 @@ void canGetClientWindows() { assertThat(windowInfo.getHeight()).isGreaterThan(0); assertThat(windowInfo.isActive()).isIn(true, false); } + + @Test + @NeedsFreshDriver + @NotYetImplemented(FIREFOX) + void canSetDownloadBehaviorAllowed() throws Exception { + Path tmpDir = TemporaryFilesystem.getDefaultTmpFS().createTempDir("downloads", "test").toPath(); + + try { + browser.setDownloadBehavior(new SetDownloadBehaviorParameters(true, tmpDir)); + + BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle()); + String url = appServer.whereIs("downloads/download.html"); + context.navigate(url, ReadinessState.COMPLETE); + + driver.findElement(By.id("file-1")).click(); + + new WebDriverWait(driver, Duration.ofSeconds(5)) + .until( + d -> { + try { + return Files.list(tmpDir) + .anyMatch(path -> path.getFileName().toString().equals("file_1.txt")); + } catch (Exception e) { + return false; + } + }); + + List fileNames = + Files.list(tmpDir) + .map(path -> path.getFileName().toString()) + .collect(Collectors.toList()); + assertThat(fileNames).contains("file_1.txt"); + } finally { + browser.setDownloadBehavior(new SetDownloadBehaviorParameters(null, (Path) null)); + } + } + + @Test + @NeedsFreshDriver + @NotYetImplemented(FIREFOX) + void canSetDownloadBehaviorDenied() throws Exception { + Path tmpDir = TemporaryFilesystem.getDefaultTmpFS().createTempDir("downloads", "test").toPath(); + + try { + browser.setDownloadBehavior(new SetDownloadBehaviorParameters(false, (Path) null)); + + BrowsingContext context = new BrowsingContext(driver, driver.getWindowHandle()); + String url = appServer.whereIs("downloads/download.html"); + context.navigate(url, ReadinessState.COMPLETE); + + driver.findElement(By.id("file-1")).click(); + + // Try to wait for file to be downloaded - should timeout + try { + new WebDriverWait(driver, Duration.ofSeconds(3), Duration.ofMillis(200)) + .until( + d -> { + try { + return Files.list(tmpDir).findAny().isPresent(); + } catch (Exception e) { + return false; + } + }); + + List files = + Files.list(tmpDir) + .map(path -> path.getFileName().toString()) + .collect(Collectors.toList()); + throw new AssertionError("A file was downloaded unexpectedly: " + files); + } catch (TimeoutException ignored) { + } + } finally { + browser.setDownloadBehavior(new SetDownloadBehaviorParameters(null, (Path) null)); + } + } + + @Test + @NeedsFreshDriver + @NotYetImplemented(FIREFOX) + void canSetDownloadBehaviorWithUserContext() throws Exception { + Path tmpDir = TemporaryFilesystem.getDefaultTmpFS().createTempDir("downloads", "test").toPath(); + String userContext = browser.createUserContext(); + + try { + BrowsingContext bc = + new BrowsingContext( + driver, new CreateContextParameters(WindowType.WINDOW).userContext(userContext)); + String contextId = bc.getId(); + + try { + driver.switchTo().window(contextId); + + browser.setDownloadBehavior( + new SetDownloadBehaviorParameters(true, tmpDir).userContexts(List.of(userContext))); + + String url = appServer.whereIs("downloads/download.html"); + bc.navigate(url, ReadinessState.COMPLETE); + + driver.findElement(By.id("file-1")).click(); + + new WebDriverWait(driver, Duration.ofSeconds(5)) + .until( + d -> { + try { + return Files.list(tmpDir) + .anyMatch(path -> path.getFileName().toString().equals("file_1.txt")); + } catch (Exception e) { + return false; + } + }); + + List files = + Files.list(tmpDir) + .map(path -> path.getFileName().toString()) + .collect(Collectors.toList()); + assertThat(files).contains("file_1.txt"); + + int initialFileCount = files.size(); + + browser.setDownloadBehavior( + new SetDownloadBehaviorParameters(false, (Path) null) + .userContexts(List.of(userContext))); + + driver.findElement(By.id("file-2")).click(); + + try { + new WebDriverWait(driver, Duration.ofSeconds(3), Duration.ofMillis(200)) + .until( + d -> { + try { + long fileCount = Files.list(tmpDir).count(); + return fileCount > initialFileCount; + } catch (Exception e) { + return false; + } + }); + + List filesAfter = + Files.list(tmpDir) + .map(path -> path.getFileName().toString()) + .collect(Collectors.toList()); + throw new AssertionError("A file was downloaded unexpectedly: " + filesAfter); + } catch (TimeoutException ignored) { + } + } finally { + browser.setDownloadBehavior( + new SetDownloadBehaviorParameters(null, (Path) null) + .userContexts(List.of(userContext))); + bc.close(); + } + } finally { + browser.removeUserContext(userContext); + } + } } From 0ecf464821c8f17e5bf9384c493ecd7babcdc6bd Mon Sep 17 00:00:00 2001 From: Delta456 Date: Fri, 28 Nov 2025 00:11:10 +0530 Subject: [PATCH 2/3] delete tmp file after use --- .../org/openqa/selenium/bidi/browser/BrowserCommandsTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java b/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java index 7fda531ebc63e..ad844b73b1ff3 100644 --- a/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java +++ b/java/test/org/openqa/selenium/bidi/browser/BrowserCommandsTest.java @@ -139,6 +139,7 @@ void canSetDownloadBehaviorAllowed() throws Exception { assertThat(fileNames).contains("file_1.txt"); } finally { browser.setDownloadBehavior(new SetDownloadBehaviorParameters(null, (Path) null)); + TemporaryFilesystem.getDefaultTmpFS().deleteTempDir(tmpDir.toFile()); } } @@ -178,6 +179,7 @@ void canSetDownloadBehaviorDenied() throws Exception { } } finally { browser.setDownloadBehavior(new SetDownloadBehaviorParameters(null, (Path) null)); + TemporaryFilesystem.getDefaultTmpFS().deleteTempDir(tmpDir.toFile()); } } @@ -257,6 +259,7 @@ void canSetDownloadBehaviorWithUserContext() throws Exception { } } finally { browser.removeUserContext(userContext); + TemporaryFilesystem.getDefaultTmpFS().deleteTempDir(tmpDir.toFile()); } } } From 1f60cb2674dc69c77dd489c65bdd9c5708866ce4 Mon Sep 17 00:00:00 2001 From: Delta456 Date: Fri, 28 Nov 2025 00:31:15 +0530 Subject: [PATCH 3/3] add license to file --- .../browser/SetDownloadBehaviorParameters.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/java/src/org/openqa/selenium/bidi/browser/SetDownloadBehaviorParameters.java b/java/src/org/openqa/selenium/bidi/browser/SetDownloadBehaviorParameters.java index c56133e3b7bd0..98bcabc79b7a4 100644 --- a/java/src/org/openqa/selenium/bidi/browser/SetDownloadBehaviorParameters.java +++ b/java/src/org/openqa/selenium/bidi/browser/SetDownloadBehaviorParameters.java @@ -1,3 +1,20 @@ +// 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.bidi.browser; import java.nio.file.Path;