Skip to content

Commit 3cc198e

Browse files
authored
fix: NPE in setInputFiles (#1113)
1 parent 071ca8b commit 3cc198e

File tree

10 files changed

+60
-30
lines changed

10 files changed

+60
-30
lines changed

playwright/src/main/java/com/microsoft/playwright/impl/ArtifactImpl.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import static com.microsoft.playwright.impl.Utils.writeToFile;
2727

2828
class ArtifactImpl extends ChannelOwner {
29-
boolean isRemote;
3029
public ArtifactImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
3130
super(parent, type, guid, initializer);
3231
}
@@ -57,15 +56,15 @@ public String failure() {
5756
}
5857

5958
public Path pathAfterFinished() {
60-
if (isRemote) {
59+
if (connection.isRemote) {
6160
throw new PlaywrightException("Path is not available when using browserType.connect(). Use download.saveAs() to save a local copy.");
6261
}
6362
JsonObject json = sendMessage("pathAfterFinished").getAsJsonObject();
6463
return FileSystems.getDefault().getPath(json.get("value").getAsString());
6564
}
6665

6766
public void saveAs(Path path) {
68-
if (isRemote) {
67+
if (connection.isRemote) {
6968
JsonObject jsonObject = sendMessage("saveAsStream").getAsJsonObject();
7069
Stream stream = connection.getExistingObject(jsonObject.getAsJsonObject("stream").get("guid").getAsString());
7170
writeToFile(stream.stream(), path);

playwright/src/main/java/com/microsoft/playwright/impl/BrowserContextImpl.java

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,6 @@ enum EventType {
8282
browser = null;
8383
}
8484
this.tracing = connection.getExistingObject(initializer.getAsJsonObject("tracing").get("guid").getAsString());
85-
tracing.isRemote = browser != null && browser.isRemote;
8685
this.request = connection.getExistingObject(initializer.getAsJsonObject("requestContext").get("guid").getAsString());
8786
}
8887

@@ -201,12 +200,6 @@ private void closeImpl() {
201200
params.addProperty("harId", entry.getKey());
202201
JsonObject json = sendMessage("harExport", params).getAsJsonObject();
203202
ArtifactImpl artifact = connection.getExistingObject(json.getAsJsonObject("artifact").get("guid").getAsString());
204-
// In case of CDP connection browser is null but since the connection is established by
205-
// the driver it is safe to consider the artifact local.
206-
if (browser() != null && browser().isRemote) {
207-
artifact.isRemote = true;
208-
}
209-
210203
// Server side will compress artifact if content is attach or if file is .zip.
211204
HarRecorder harParams = entry.getValue();
212205
boolean isCompressed = harParams.contentPolicy == HarContentPolicy.ATTACH || harParams.path.toString().endsWith(".zip");

playwright/src/main/java/com/microsoft/playwright/impl/BrowserImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
class BrowserImpl extends ChannelOwner implements Browser {
3939
final Set<BrowserContextImpl> contexts = new HashSet<>();
4040
private final ListenerCollection<EventType> listeners = new ListenerCollection<>();
41-
boolean isRemote;
4241
boolean isConnectedOverWebSocket;
4342
private boolean isConnected = true;
4443
BrowserTypeImpl browserType;

playwright/src/main/java/com/microsoft/playwright/impl/BrowserTypeImpl.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ private Browser connectImpl(String wsEndpoint, ConnectOptions options) {
9797
}
9898
playwright.initSharedSelectors(this.connection.getExistingObject("Playwright"));
9999
BrowserImpl browser = connection.getExistingObject(playwright.initializer.getAsJsonObject("preLaunchedBrowser").get("guid").getAsString());
100-
browser.isRemote = true;
101100
browser.isConnectedOverWebSocket = true;
102101
browser.browserType = this;
103102
Consumer<JsonPipe> connectionCloseListener = t -> browser.notifyRemoteClosed();
@@ -132,7 +131,6 @@ private Browser connectOverCDPImpl(String endpointURL, ConnectOverCDPOptions opt
132131
JsonObject json = sendMessage("connectOverCDP", params).getAsJsonObject();
133132

134133
BrowserImpl browser = connection.getExistingObject(json.getAsJsonObject("browser").get("guid").getAsString());
135-
browser.isRemote = true;
136134
browser.browserType = this;
137135
if (json.has("defaultContext")) {
138136
String contextId = json.getAsJsonObject("defaultContext").get("guid").getAsString();

playwright/src/main/java/com/microsoft/playwright/impl/Connection.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ public class Connection {
5555
private final Transport transport;
5656
private final Map<String, ChannelOwner> objects = new HashMap<>();
5757
private final Root root;
58+
final boolean isRemote;
5859
private int lastId = 0;
5960
private final StackTraceCollector stackTraceCollector;
6061
private final Map<Integer, WaitableResult<JsonElement>> callbacks = new HashMap<>();
@@ -81,12 +82,17 @@ Playwright initialize() {
8182
}
8283

8384
Connection(Transport pipe, Map<String, String> env, LocalUtils localUtils) {
84-
this(pipe, env);
85+
this(pipe, env, true);
8586
this.localUtils = localUtils;
8687
}
8788

8889
Connection(Transport transport, Map<String, String> env) {
90+
this(transport, env, false);
91+
}
92+
93+
private Connection(Transport transport, Map<String, String> env, boolean isRemote) {
8994
this.env = env;
95+
this.isRemote = isRemote;
9096
if (isLogging) {
9197
transport = new TransportLogger(transport);
9298
}

playwright/src/main/java/com/microsoft/playwright/impl/PageImpl.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ protected void handleEvent(String event, JsonObject params) {
151151
} else if ("download".equals(event)) {
152152
String artifactGuid = params.getAsJsonObject("artifact").get("guid").getAsString();
153153
ArtifactImpl artifact = connection.getExistingObject(artifactGuid);
154-
artifact.isRemote = browserContext.browser() != null && browserContext.browser().isRemote;
155154
DownloadImpl download = new DownloadImpl(this, artifact, params);
156155
listeners.notify(EventType.DOWNLOAD, download);
157156
} else if ("fileChooser".equals(event)) {

playwright/src/main/java/com/microsoft/playwright/impl/TracingImpl.java

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,6 @@
2626
import static com.microsoft.playwright.impl.Serialization.gson;
2727

2828
class TracingImpl extends ChannelOwner implements Tracing {
29-
boolean isRemote;
30-
3129
TracingImpl(ChannelOwner parent, String type, String guid, JsonObject initializer) {
3230
super(parent, type, guid, initializer);
3331
}
@@ -36,7 +34,7 @@ private void stopChunkImpl(Path path) {
3634
JsonObject params = new JsonObject();
3735
String mode = "doNotSave";
3836
if (path != null) {
39-
if (isRemote) {
37+
if (connection.isRemote) {
4038
mode = "compressTrace";
4139
} else {
4240
mode = "compressTraceAndSources";
@@ -48,16 +46,13 @@ private void stopChunkImpl(Path path) {
4846
return;
4947
}
5048
ArtifactImpl artifact = connection.getExistingObject(json.getAsJsonObject("artifact").get("guid").getAsString());
51-
// In case of CDP connection browser is null but since the connection is established by
52-
// the driver it is safe to consider the artifact local.
53-
if (isRemote) {
54-
artifact.isRemote = true;
55-
}
5649
artifact.saveAs(path);
5750
artifact.delete();
5851

5952
// Add local sources to the remote trace if necessary.
60-
if (isRemote && json.has("sourceEntries")) {
53+
// In case of CDP connection since the connection is established by
54+
// the driver it is safe to consider the artifact local.
55+
if (connection.isRemote && json.has("sourceEntries")) {
6156
JsonArray entries = json.getAsJsonArray("sourceEntries");
6257
connection.localUtils.zip(path, entries);
6358
}

playwright/src/main/java/com/microsoft/playwright/impl/Utils.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ static boolean hasLargeFile(Path[] files) {
165165
}
166166

167167
static void addLargeFileUploadParams(Path[] files, JsonObject params, BrowserContextImpl context) {
168-
if (context.browser().isRemote) {
168+
if (context.connection.isRemote) {
169169
List<WritableStream> streams = new ArrayList<>();
170170
JsonArray jsonStreams = new JsonArray();
171171
for (Path path : files) {

playwright/src/main/java/com/microsoft/playwright/impl/VideoImpl.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,13 @@
2727
class VideoImpl implements Video {
2828
private final PageImpl page;
2929
private final WaitableResult<ArtifactImpl> waitableArtifact = new WaitableResult<>();
30-
private final boolean isRemote;
3130

3231
VideoImpl(PageImpl page) {
3332
this.page = page;
3433
BrowserImpl browser = page.context().browser();
35-
isRemote = browser != null && browser.isRemote;
3634
}
3735

3836
void setArtifact(ArtifactImpl artifact) {
39-
artifact.isRemote = isRemote;
4037
waitableArtifact.complete(artifact);
4138
}
4239

@@ -58,7 +55,7 @@ public void delete() {
5855
@Override
5956
public Path path() {
6057
return page.withLogging("Video.path", () -> {
61-
if (isRemote) {
58+
if (page.connection.isRemote) {
6259
throw new PlaywrightException("Path is not available when using browserType.connect(). Use saveAs() to save a local copy.");
6360
}
6461
try {

playwright/src/test/java/com/microsoft/playwright/TestDefaultBrowserContext2.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,19 @@
22

33
import com.microsoft.playwright.options.Geolocation;
44
import org.junit.jupiter.api.AfterEach;
5+
import org.junit.jupiter.api.Assumptions;
56
import org.junit.jupiter.api.Test;
67
import org.junit.jupiter.api.condition.DisabledIf;
78
import org.junit.jupiter.api.io.TempDir;
89

910
import java.io.IOException;
11+
import java.io.OutputStreamWriter;
12+
import java.io.Writer;
1013
import java.nio.file.Files;
1114
import java.nio.file.Path;
15+
import java.util.Collections;
1216
import java.util.List;
17+
import java.util.concurrent.CompletableFuture;
1318
import java.util.concurrent.ExecutionException;
1419
import java.util.concurrent.Future;
1520
import java.util.stream.Collectors;
@@ -251,4 +256,43 @@ void shouldRespectSelectors() {
251256
assertEquals("hello", page.innerHTML("defaultContextCSS=div"));
252257
}
253258

254-
}
259+
@Test
260+
void shouldUploadLargeFile(@TempDir Path tmpDir) throws IOException, ExecutionException, InterruptedException {
261+
Assumptions.assumeTrue(3 <= (Runtime.getRuntime().maxMemory() >> 30), "Fails if max heap size is < 3Gb");
262+
Page page = launchPersistent();
263+
page.navigate(server.PREFIX + "/input/fileupload.html");
264+
Path uploadFile = tmpDir.resolve("200MB.zip");
265+
String str = String.join("", Collections.nCopies(4 * 1024, "A"));
266+
267+
try (Writer stream = new OutputStreamWriter(Files.newOutputStream(uploadFile))) {
268+
for (int i = 0; i < 50 * 1024; i++) {
269+
stream.write(str);
270+
}
271+
}
272+
Locator input = page.locator("input[type='file']");
273+
JSHandle events = input.evaluateHandle("e => {\n" +
274+
" const events = [];\n" +
275+
" e.addEventListener('input', () => events.push('input'));\n" +
276+
" e.addEventListener('change', () => events.push('change'));\n" +
277+
" return events;\n" +
278+
" }");
279+
input.setInputFiles(uploadFile);
280+
assertEquals("200MB.zip", input.evaluate("e => e.files[0].name"));
281+
assertEquals(asList("input", "change"), events.evaluate("e => e"));
282+
CompletableFuture<MultipartFormData> formData = new CompletableFuture<>();
283+
server.setRoute("/upload", exchange -> {
284+
try {
285+
MultipartFormData multipartFormData = MultipartFormData.parseRequest(exchange);
286+
formData.complete(multipartFormData);
287+
} catch (Exception e) {
288+
e.printStackTrace();
289+
formData.completeExceptionally(e);
290+
}
291+
exchange.sendResponseHeaders(200, -1);
292+
});
293+
page.click("input[type=submit]", new Page.ClickOptions().setTimeout(90_000));
294+
List<MultipartFormData.Field> fields = formData.get().fields;
295+
assertEquals(1, fields.size());
296+
assertEquals("200MB.zip", fields.get(0).filename);
297+
assertEquals(200 * 1024 * 1024, fields.get(0).content.length());
298+
}}

0 commit comments

Comments
 (0)